Commit d3945c0d by Gelencsér Szabolcs

New dashboard concept. #TODO: implement generateVmHTML in dashboard.js.…

New dashboard concept. #TODO: implement generateVmHTML in dashboard.js. (searching for a vm ruins the vm list currently)
parent dad93220
......@@ -30,6 +30,7 @@ $(function() {
/* operations */
$('#ops, #vm-details-resources-disk, #vm-details-renew-op, #vm-details-pw-reset, #vm-details-add-interface, .operation-wrapper').on('click', '.operation', function(e) {
var icon = $(this).children("i").addClass('fa-spinner fa-spin');
var redirect_on_success = $(this).data("redirect-on-success");
$.ajax({
type: 'GET',
......@@ -42,6 +43,10 @@ $(function() {
$('#confirmation-modal').remove();
});
$('#vm-migrate-node-list li input:checked').closest('li').addClass('panel-primary');
if(redirect_on_success) {
var submit_button = $('#confirmation-modal button[type=submit]')
submit_button.data("redirect-on-success", redirect_on_success);
}
}
});
e.preventDefault();
......@@ -50,6 +55,7 @@ $(function() {
/* if the operation fails show the modal again */
$("body").on("click", "#confirmation-modal #op-form-send", function() {
var url = $(this).closest("form").prop("action");
var redirect_on_success = $(this).data("redirect-on-success");
$.ajax({
url: url,
......@@ -75,6 +81,10 @@ $(function() {
if(data.messages && data.messages.length > 0) {
addMessage(data.messages.join("<br />"), data.success ? "success" : "danger");
}
if(redirect_on_success) {
window.location.href = redirect_on_success;
}
}
else {
/* if the post was not successful wait for the modal to disappear
......
......@@ -98,6 +98,7 @@ $(function () {
return false;
});
$('body .dashboard-vm-collapse-caret').tooltip({'placement': 'right'});
$('body .title-favourite').tooltip({'placement': 'right'});
$('body :input[title]').tooltip({trigger: 'focus', placement: 'auto right'});
$('body [title]').tooltip();
......@@ -121,6 +122,54 @@ $(function () {
$('.no-js-hidden').show();
$('.js-hidden').hide();
/* dropdown caret */
$("#dashboard-vm-list").on('click', '.dashboard-vm-collapse-caret', function(e) {
var isCollapsed = $(this).hasClass("fa-caret-down");
var pk = $(this).data("pk");
var collapseBody = $("#dashboard-vm-collapse-body-" + pk);
if(isCollapsed)
{
$(this).removeClass("fa-caret-down").addClass("fa-caret-up");
collapseBody.removeClass("dashboard-vm-collapse-body-collapsed");
}
else
{
$(this).removeClass("fa-caret-up").addClass("fa-caret-down");
collapseBody.addClass("dashboard-vm-collapse-body-collapsed");
}
return false;
});
/* select input's text on focus for easy copy */
$("#dashboard-vm-list").on('focus', 'input', function(e) {
$(this).select();
return false;
});
/* show password button */
$("#dashboard-vm-list").on('click', '.show-password', function(e) {
var passwordInput = $(this).siblings(".password-input");
var inputType = passwordInput.prop("type");
var icon = $(this).children('i');
if(inputType == "password")
{
passwordInput.prop("type", "text").focus();
icon.removeClass("fa-eye").addClass("fa-eye-slash");
}
else
{
passwordInput.prop("type", "password");
icon.removeClass("fa-eye-slash").addClass("fa-eye");
}
return false;
});
/* favourite star */
$("#dashboard-vm-list, .page-header").on('click', '.dashboard-vm-favourite', function(e) {
var star = $(this).children("i");
......
......@@ -197,10 +197,6 @@ html {
text-align: center;
}
.dashboard-index .panel {
height: 294px;
}
#vm-details-rename, #vm-details-h1-name, #vm-details-rename ,
#node-details-rename, #node-details-rename *, #node-details-h1-name, #node-list-rename, #node-list-rename *#group-details-rename, #group-details-rename *, #group-details-h1-name, #group-list-rename, #group-list-rename * {
......@@ -584,8 +580,12 @@ footer a, footer a:hover, footer a:visited {
margin: 0;
}
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list,
#dashboard-template-list, #dashboard-files-toplist, #dashboard-user-list {
#dashboard-vm-list, #dashboard-group-list {
min-height: 254px;
}
#dashboard-node-list, #dashboard-template-list,
#dashboard-files-toplist, #dashboard-user-list {
min-height: 200px;
}
......@@ -1175,6 +1175,40 @@ textarea[name="new_members"] {
#dashboard-vm-list {
.list-group-item {
display: flex;
margin-bottom: -7px;
}
.dashboard-vm-collapse-body {
padding: 10px 15px;
.list-group-item {
display: block;
}
.form-group {
margin: 0;
}
}
.dashboard-vm-collapse-caret {
cursor: pointer;
font-size: large;
position: relative;
top: 2px;
}
.dashboard-vm-collapse-body-collapsed {
height: 0;
padding: 0;
visibility: hidden;
margin: 0;
* {
height: 0;
visibility: hidden;
padding: 0;
margin: 0;
}
}
.index-vm-list-name, .index-vm-list-host {
......@@ -1191,8 +1225,32 @@ textarea[name="new_members"] {
padding-top: 3px;
flex: 1;
}
}
.input-xs {
height: 22px;
padding: 2px 8px;
font-size: 11px;
}
.label-xs {
font-size: 11px;
position: relative;
top: -5px;
}
.multiline-list-group-item-fix {
display: block;
}
.vm-multiline-info {
font-size: 11.9px;
margin-left: 20px;
}
.pull-right-center-fix {
margin-top: 4px;
}
}
#dashboard-user-list {
.list-group-item {
......
......@@ -2,14 +2,6 @@
<div class="panel panel-default">
<div class="panel-heading">
<div class="pull-right toolbar">
<div class="btn-group">
<a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"
data-container="body"
title="{% trans "summary view" %}"><i class="fa fa-dashboard"></i></a>
<a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"
data-container="body"
title="{% trans "list view" %}"><i class="fa fa-list"></i></a>
</div>
<span class="btn btn-default btn-xs infobtn" data-container="body" title="{% trans "List of your current virtual machines. Favourited ones are ahead of others." %}"><i class="fa fa-info-circle"></i></span>
</div>
<h3 class="no-margin">
......@@ -27,24 +19,101 @@
<div id="dashboard-vm-list">
{% for i in instances %}
<a href="{{ i.get_absolute_url }}" class="list-group-item
{% if forloop.last and instances|length < 5 %} list-group-item-last{% endif %}">
<span class="index-vm-list-name">
<i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }}
</span>
<small class="text-muted index-vm-list-host">
{% if i.owner == request.user %}{{ i.short_hostname }}
{% else %}{{i.owner.profile.get_display_name}}{% endif %}
</small>
<div class="pull-right dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %}
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
{% else %}
<i class="fa fa-star-o text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i>
{% endif %}
{% if forloop.last and instances|length < 5 %} list-group-item-last{% endif %} multiline-list-group-item-fix">
<div>
<span class="index-vm-list-name">
<i class="fa {{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }}
</span>
<small class="text-muted index-vm-list-host">
{% if i.owner == request.user %}{{ i.short_hostname }}
{% else %}{{i.owner.profile.get_display_name}}{% endif %}
</small>
<div class="pull-right operation-wrapper pull-right-center-fix">
<!-- Preferred operation -->
<span
href="{{i.pref_operation.get_url}}"
data-redirect-on-success="vm/{{ i.pk }}"
class="operation operation-{{i.pref_operation.op}} btn btn-{{i.pref_operation.effect}} btn-xs"
title="{{i.pref_operation.name|capfirst}}: {{i.pref_operation.description|truncatewords:15}}">
<i class="fa fa-{{i.pref_operation.icon}}"></i>
<span>{{i.pref_operation.name}}</span>
</span>
<!-- (Un)Favourite -->
<span class="dashboard-vm-favourite" data-vm="{{ i.pk }}">
{% if i.fav %}
<i class="fa fa-star text-primary title-favourite" title="{% trans "Unfavourite" %}"></i>
{% else %}
<i class="fa fa-star-o text-primary title-favourite" title="{% trans "Mark as favorite" %}"></i>
{% endif %}
</span>
<!-- Collapse's caret -->
<i class="fa fa-caret-down dashboard-vm-collapse-caret" data-pk="{{ i.pk }}" title="{% trans "IndexVmDropDown" %}"></i>
</div>
</div>
<div style="clear: both;"></div>
<div class="text-muted vm-multiline-info">
RAM: {{ i.ram_size }}, CPU: {{ i.num_cores }}, IP: {{ i.get_connect_host }}
</div>
<div style="clear: both;"></div>
</a>
<div id="dashboard-vm-collapse-body-{{ i.pk }}" class="dashboard-vm-collapse-body dashboard-vm-collapse-body-collapsed">
<form class="form-horizontal">
<!-- IPv4 -->
<div class="form-group">
<label for="connectionInfoIpv4-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "IPv4" %}</label>
<div class="col-sm-9">
{% if i.get_connect_port %}
<input type="text" class="form-control input-xs" id="connectionInfoIpv4-{{ i.pk }}"
value="cloud@{{ i.get_connect_host }}:{{ i.get_connect_port }}">
{% elif i.interface_set.count < 1%}
<input type="text" class="form-control input-xs" disabled id="connectionInfoIpv4-{{ i.pk }}"
value="{% trans "The VM doesn't have any network interface." %}">
{% else %}
<input type="text" class="form-control input-xs" disabled id="connectionInfoIpv4-{{ i.pk }}"
value="{% trans "The required port for this protocol is not forwarded." %}">
{% endif %}
</div>
</div>
<!-- IPv6 -->
{% if i.ipv6 and i.get_connect_port %}
<div class="form-group">
<label for="connectionInfoIpv6-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "IPv6" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control input-xs" id="connectionInfoIpv6-{{ i.pk }}"
value="cloud@{{ i.ipv6 }}:{{ i.get_connect_port }}">
</div>
</div>
{% endif %}
<!-- Password -->
<div class="form-group">
<label for="password-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "Password" %}</label>
<div class=" col-sm-9">
<div class="input-group" id="password-{{ i.pk }}">
<input type="password" class="password-input form-control input-xs"
value="{{ i.pw }}" spellcheck="false"/>
<span class="show-password input-group-addon btn input-xs"
title="{% trans "Show password" %}">
<i class="fa fa-eye"></i>
</span>
</div>
</div>
</div>
<!-- Command -->
{% for command in i.connect_commands %}
<div class="form-group">
<label for="command-{{ i.pk }}" class="col-sm-3 control-label label-xs">{% trans "Command" %}</label>
<div class="col-sm-9">
<input type="text" class="form-control input-xs" id="command-{{ i.pk }}"
value="{{ command }}">
</div>
</div>
{% endfor %}
</form>
</div>
{% empty %}
<div class="list-group-item list-group-item-last">
{% trans "You have no virtual machines." %}
......
......@@ -16,7 +16,7 @@
<div class="row">
{% if perms.vm %}
<div class="col-lg-4 col-sm-6">
<div class="col-lg-6 col-sm-6">
{% include "dashboard/index-vm.html" %}
</div>
{% else %}
......@@ -25,18 +25,22 @@
</div>
{% endif %}
{% if perms.auth %}
<div class="col-lg-4 col-sm-6">
<div class="col-lg-6 col-sm-6">
{% include "dashboard/index-groups.html" %}
</div>
{% endif %}
</div>
<div class="row">
{% if not no_store %}
<div class="col-lg-4 col-sm-6">
{% include "dashboard/store/index-files.html" %}
</div>
{% endif %}
{% if perms.vm.create_template %}
<div class="col-lg-4 col-sm-6">
{% include "dashboard/index-templates.html" %}
......
......@@ -20,6 +20,7 @@ import logging
from django.core.cache import get_cache
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
from django.conf import settings
from django.contrib.auth.models import Group, User
from django.views.generic import TemplateView
......@@ -34,6 +35,21 @@ from ..store_api import Store
logger = logging.getLogger(__name__)
def get_operations(instance, user):
ops = []
for k, v in vm_ops.iteritems():
try:
op = v.get_op_by_object(instance)
op.check_auth(user)
op.check_precond()
except PermissionDenied as e:
logger.debug('Not showing operation %s for %s: %s',
k, instance, unicode(e))
except Exception:
ops.append(v.bind_to_object(instance, disabled=True))
else:
ops.append(v.bind_to_object(instance))
return ops
class IndexView(LoginRequiredMixin, TemplateView):
template_name = "dashboard/index.html"
......@@ -49,9 +65,16 @@ class IndexView(LoginRequiredMixin, TemplateView):
display = list(favs) + list(set(instances) - set(favs))
for d in display:
d.fav = True if d in favs else False
for i_pos in range(0, min(instances.count(), 5)):
instances[i_pos].connect_commands = user.profile.get_connect_commands(instances[i_pos])
operations = get_operations(instances[i_pos], user)
instances[i_pos].pref_operation = next(oper for oper in operations \
if oper.is_preferred and not hasattr(oper, 'disabled') )
context.update({
'instances': display[:5],
'more_instances': instances.count() - len(instances[:5])
'more_instances': instances.count() - len(instances[:5]),
})
running = instances.filter(status='RUNNING')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment