Commit ad1519f3 by Bach Dániel

Merge branch 'feature-indexview-fixes' into 'master'

Few fixes in index view

- fix vm list height
- permanent vm list button
- mark %d more buttons for translation (fixes #133)
parents e1827d58 c2b0ca67
...@@ -217,7 +217,7 @@ class AclBase(Model): ...@@ -217,7 +217,7 @@ class AclBase(Model):
clsfilter = Q(object_level_set__in=ols.all()) clsfilter = Q(object_level_set__in=ols.all())
if owner_also: if owner_also:
clsfilter |= Q(owner=user) clsfilter |= Q(owner=user)
return cls.objects.filter(clsfilter) return cls.objects.filter(clsfilter).distinct()
@classmethod @classmethod
def get_objects_with_group_level(cls, level, group): def get_objects_with_group_level(cls, level, group):
......
...@@ -608,6 +608,11 @@ footer a, footer a:hover, footer a:visited { ...@@ -608,6 +608,11 @@ footer a, footer a:hover, footer a:visited {
margin: 0; margin: 0;
} }
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list,
#dashboard-template-list {
min-height: 204px;
}
#group-detail-user-table td:first-child, #group-detail-user-table th:last-child, #group-detail-user-table td:first-child, #group-detail-user-table th:last-child,
#group-detail-user-table td:last-child, #group-detail-user-table td:last-child,
#group-detail-perm-table td:first-child, #group-detail-perm-table th:last-child, #group-detail-perm-table td:first-child, #group-detail-perm-table th:last-child,
...@@ -630,3 +635,22 @@ textarea[name="list-new-namelist"] { ...@@ -630,3 +635,22 @@ textarea[name="list-new-namelist"] {
.table thead>tr>th { .table thead>tr>th {
border-bottom: 1px; border-bottom: 1px;
} }
.badge-pulse {
-webkit-animation-name: 'pulse_animation';
-webkit-animation-duration: 1000ms;
-webkit-transform-origin: 70% 70%;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
@-webkit-keyframes pulse_animation {
0% { -webkit-transform: scale(1); }
30% { -webkit-transform: scale(1); }
40% { -webkit-transform: scale(1.18); }
50% { -webkit-transform: scale(1); }
60% { -webkit-transform: scale(1); }
70% { -webkit-transform: scale(1.08); }
80% { -webkit-transform: scale(1); }
100% { -webkit-transform: scale(1); }
}
...@@ -253,9 +253,10 @@ $(function () { ...@@ -253,9 +253,10 @@ $(function () {
for(var i=0; i<5 && i<search_result.length; i++) for(var i=0; i<5 && i<search_result.length; i++)
html += generateVmHTML(search_result[i].pk, search_result[i].name, html += generateVmHTML(search_result[i].pk, search_result[i].name,
search_result[i].host, search_result[i].icon, search_result[i].host, search_result[i].icon,
search_result[i].status, search_result[i].fav); search_result[i].status, search_result[i].fav,
(search_result.length < 5));
if(search_result.length == 0) if(search_result.length == 0)
html += '<div class="list-group-item">No result</div>'; html += '<div class="list-group-item list-group-item-last">' + gettext("No result") + '</div>';
$("#dashboard-vm-list").html(html); $("#dashboard-vm-list").html(html);
$('.title-favourite').tooltip({'placement': 'right'}); $('.title-favourite').tooltip({'placement': 'right'});
...@@ -268,6 +269,46 @@ $(function () { ...@@ -268,6 +269,46 @@ $(function () {
} }
}); });
/* search for groups */
var my_groups = []
$("#dashboard-group-search-input").keyup(function(e) {
// if my_groups is empty get a list of our groups
if(my_groups.length < 1) {
$.ajaxSetup( { "async": false } );
$.get("/dashboard/group/list/", function(result) {
for(var i in result) {
my_groups.push({
'url': result[i].url,
'name': result[i].name.toLowerCase(),
});
}
});
$.ajaxSetup( { "async": true } );
}
input = $("#dashboard-group-search-input").val().toLowerCase();
var search_result = []
var html = '';
for(var i in my_groups) {
if(my_groups[i].name.indexOf(input) != -1) {
search_result.push(my_groups[i]);
}
}
for(var i=0; i<5 && i<search_result.length; i++)
html += generateGroupHTML(search_result[i].url, search_result[i].name);
if(search_result.length == 0)
html += '<div class="list-group-item">No result</div>';
$("#dashboard-group-list").html(html);
// if there is only one result and ENTER is pressed redirect
if(e.keyCode == 13 && search_result.length == 1) {
window.location.href = search_result[0].url;
}
if(e.keyCode == 13 && search_result.length > 1 && input.length > 0) {
window.location.href = "/dashboard/group/list/?s=" + input;
}
});
/* notification message toggle */ /* notification message toggle */
$(document).on('click', ".notification-message-subject", function() { $(document).on('click', ".notification-message-subject", function() {
$(".notification-message-text", $(this).parent()).slideToggle(); $(".notification-message-text", $(this).parent()).slideToggle();
...@@ -275,12 +316,14 @@ $(function () { ...@@ -275,12 +316,14 @@ $(function () {
}); });
$("#notification-button a").click(function() { $("#notification-button a").click(function() {
$('.notification-messages').load("/dashboard/notifications/"); $('.notification-messages').load("/dashboard/notifications/");
$('#notification-button a span[class*="badge-pulse"]').remove();
}); });
}); });
function generateVmHTML(pk, name, host, icon, _status, fav) { function generateVmHTML(pk, name, host, icon, _status, fav, is_last) {
return '<a href="/dashboard/vm/' + pk + '/" class="list-group-item">' + return '<a href="/dashboard/vm/' + pk + '/" class="list-group-item' +
(is_last ? ' list-group-item-last' : '') + '">' +
'<span class="index-vm-list-name">' + '<span class="index-vm-list-name">' +
'<i class="' + icon + '" title="' + _status + '"></i> ' + name + '<i class="' + icon + '" title="' + _status + '"></i> ' + name +
'</span>' + '</span>' +
...@@ -293,6 +336,12 @@ function generateVmHTML(pk, name, host, icon, _status, fav) { ...@@ -293,6 +336,12 @@ function generateVmHTML(pk, name, host, icon, _status, fav) {
'</a>'; '</a>';
} }
function generateGroupHTML(url, name) {
return '<a href="' + url + '" class="list-group-item real-link">'+
'<i class="icon-group"></i> '+ name +
'</a>';
}
/* copare vm-s by fav, pk order */ /* copare vm-s by fav, pk order */
function compareVmByFav(a, b) { function compareVmByFav(a, b) {
if(a.fav && b.fav) { if(a.fav && b.fav) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content=""> <meta name="description" content="">
<meta name="author" content=""> <meta name="author" content="">
<link rel="icon" type="image/png" href="{{ STATIC_URL}}dashboard/img/favicon.png"/>
<title>{% block title %}{% block title-page %}{% endblock %} | {% block title-site %}CIRCLE{% endblock %}{% endblock %}</title> <title>{% block title %}{% block title-page %}{% endblock %} | {% block title-site %}CIRCLE{% endblock %}{% endblock %}</title>
...@@ -39,7 +40,7 @@ ...@@ -39,7 +40,7 @@
<ul class="nav navbar-nav pull-right"> <ul class="nav navbar-nav pull-right">
<li class="dropdown" id="notification-button"> <li class="dropdown" id="notification-button">
<a href="{% url "dashboard.views.notifications" %}" style="color: white; font-size: 12px;" class="dropdown-toggle" data-toggle="dropdown"> <a href="{% url "dashboard.views.notifications" %}" style="color: white; font-size: 12px;" class="dropdown-toggle" data-toggle="dropdown">
{% trans "Notifications" %}{% if NEW_NOTIFICATIONS_COUNT > 0 %} <span class="badge">{{ NEW_NOTIFICATIONS_COUNT }}</span>{% endif %} {% trans "Notifications" %}{% if NEW_NOTIFICATIONS_COUNT > 0 %} <span class="badge badge-pulse">{{ NEW_NOTIFICATIONS_COUNT }}</span>{% endif %}
</a> </a>
<ul class="dropdown-menu notification-messages"> <ul class="dropdown-menu notification-messages">
<li>{% trans "Loading..." %}</li> <li>{% trans "Loading..." %}</li>
......
...@@ -2,31 +2,41 @@ ...@@ -2,31 +2,41 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<div class="pull-right toolbar"> <div class="pull-right toolbar">
<span class="btn btn-default btn-xs infobtn" title="{% trans "Help" %}"><i class="icon-info-sign"></i></span> <span class="btn btn-default btn-xs infobtn" title="{% trans "List of groups that you have access to." %}"><i class="icon-info-sign"></i></span>
</div> </div>
<h3 class="no-margin"><i class="icon-group"></i> {% trans "Groups" %}</h3> <h3 class="no-margin"><i class="icon-group"></i> {% trans "Groups" %}</h3>
</div> </div>
<div class="list-group" id="vm-list-view"> <div class="list-group" id="vm-list-view">
{% for i in groups %} <div id="dashboard-group-list">
<a href="{% url "dashboard.views.group-detail" pk=i.pk %}" class="list-group-item real-link"> {% for i in groups %}
<a href="{% url "dashboard.views.group-detail" pk=i.pk %}" class="list-group-item real-link
{% if forloop.last and groups|length < 5 %} list-group-item-last{% endif %}">
<i class="icon-group"></i> {{ i.name }} <i class="icon-group"></i> {{ i.name }}
</a> </a>
{% endfor %} {% endfor %}
</div>
<div href="#" class="list-group-item list-group-footer text-right"> <div href="#" class="list-group-item list-group-footer text-right">
<div class="row"> <div class="row">
<div class="col-sm-6 col-xs-6 input-group input-group-sm"> <div class="col-sm-6 col-xs-6 input-group input-group-sm">
<input type="text" class="form-control" placeholder="{% trans "Search..." %}" /> <input id="dashboard-group-search-input" type="text" class="form-control" placeholder="{% trans "Search..." %}" />
<div class="input-group-btn"> <div class="input-group-btn">
<button type="submit" class="form-control btn btn-primary" title="search"><i class="icon-search"></i></button> <button type="submit" class="form-control btn btn-primary"><i class="icon-search"></i></button>
</div> </div>
</div> </div>
<div class="col-sm-6 text-right"> <div class="col-sm-6 text-right">
{% if more_groups >= 0 %}
<a class="btn btn-primary btn-xs" href="{% url "dashboard.views.group-list" %}"> <a class="btn btn-primary btn-xs" href="{% url "dashboard.views.group-list" %}">
<i class="icon-chevron-sign-right"></i> <strong>{{ more_groups }}</strong> more <i class="icon-chevron-sign-right"></i>
{% if more_groups > 0 %}
{% blocktrans count more=more_groups %}
<strong>{{ more }}</strong> more
{% plural %}
<strong>{{ more }}</strong> more
{% endblocktrans %}
{% else %}
{% trans "list" %}
{% endif %}
</a> </a>
{% endif %} <a class="btn btn-success btn-xs group-create" href="{% url "dashboard.views.group-create" %}"><i class="icon-plus-sign"></i> {% trans "new" %} </a>
<a class="btn btn-success btn-xs group-create" href="{% url "dashboard.views.group-create" %}"><i class="icon-upload-alt"></i> {% trans "new" %} </a>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -14,11 +14,14 @@ ...@@ -14,11 +14,14 @@
</h3> </h3>
</div > </div >
<div class="list-group" id="node-list-view"> <div class="list-group" id="node-list-view">
{% for i in nodes %} <div id="dashboard-node-list">
<a href="{% url "dashboard.views.node-detail" pk=i.pk %}" class="list-group-item"> {% for i in nodes %}
<i class="icon-{% if i.enabled == True %}play-sign{% else %}pause{% endif %}"></i> {{ i.name }} <a href="{% url "dashboard.views.node-detail" pk=i.pk %}" class="list-group-item
</a> {% if forloop.last and nodes|length < 5 %} list-group-item-last{% endif %}">
{% endfor %} <i class="icon-{% if i.enabled == True %}play-sign{% else %}pause{% endif %}"></i> {{ i.name }}
</a>
{% endfor %}
</div>
<div href="#" class="list-group-item list-group-footer"> <div href="#" class="list-group-item list-group-footer">
<div class="row"> <div class="row">
<div class="col-sm-6 col-xs-6 input-group input-group-sm"> <div class="col-sm-6 col-xs-6 input-group input-group-sm">
......
{% load i18n %} {% load i18n %}
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<span class="btn btn-default btn-xs infobtn pull-right" title="{% trans "Help" %}"> <span class="btn btn-default btn-xs infobtn pull-right" title="{% trans "List of VM templates that are available for you. You can create new ones from scratch or customize existing ones (preferred)." %}">
<i class="icon-info-sign"></i> <i class="icon-info-sign"></i>
</span> </span>
<h3 class="no-margin"><i class="icon-puzzle-piece"></i> {% trans "Templates" %} <h3 class="no-margin"><i class="icon-puzzle-piece"></i> {% trans "Templates" %}
</h3> </h3>
</div> </div>
<div class="list-group" id="dashboard-template-list"> <div class="list-group" id="dashboard-template-list">
{% for t in templates %} <div id="dashboard-template-list">
<a href="{% url "dashboard.views.template-detail" pk=t.pk %}" class="list-group-item"> {% for t in templates %}
<span class="index-template-list-name"> <a href="{% url "dashboard.views.template-detail" pk=t.pk %}" class="list-group-item
<i class="icon-{{ t.os_type }}"></i> {{ t.name }} {% if forloop.last and nodes|length < 5 %} list-group-item-last{% endif %}">
</span> <span class="index-template-list-name">
<small class="text-muted index-template-list-system">{{ t.system }}</small> <i class="icon-{{ t.os_type }}"></i> {{ t.name }}
<div class="pull-right vm-create" data-template="{{ t.pk }}"><i title="{% trans "Start vm instance" %}" class="icon-play"></i></div> </span>
<div class="clearfix"></div> <small class="text-muted index-template-list-system">{{ t.system }}</small>
</a> <div class="pull-right vm-create" data-template="{{ t.pk }}"><i title="{% trans "Start vm instance" %}" class="icon-play"></i></div>
{% empty %} <div class="clearfix"></div>
<div class="alert alert-warning" style="margin: 10px;"> </a>
<p> {% empty %}
{% trans "You don't have any templates, however you can still start virtual machines and even save them as new templates!" %} <div class="alert alert-warning" style="margin: 10px;">
</p> <p>
</div> {% trans "You don't have any templates, however you can still start virtual machines and even save them as new templates!" %}
{% endfor %} </p>
</div>
{% endfor %}
</div>
<div href="#" class="list-group-item list-group-footer text-right"> <div href="#" class="list-group-item list-group-footer text-right">
<p> <p>
<a href="{% url "dashboard.views.template-list" %}" class="btn btn-primary btn-xs"> <a href="{% url "dashboard.views.template-list" %}" class="btn btn-primary btn-xs">
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"><i class="icon-dashboard"></i></a> <a href="#index-graph-view" data-index-box="vm" class="btn btn-default btn-xs"><i class="icon-dashboard"></i></a>
<a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"><i class="icon-list"></i></a> <a href="#index-list-view" data-index-box="vm" class="btn btn-default btn-xs disabled"><i class="icon-list"></i></a>
</div> </div>
<span class="btn btn-default btn-xs infobtn" title="{% trans "Help" %}"><i class="icon-info-sign"></i></span> <span class="btn btn-default btn-xs infobtn" title="{% trans "List of your current virtual machines. Favourited ones are ahead of others." %}"><i class="icon-info-sign"></i></span>
</div> </div>
<h3 class="no-margin"> <h3 class="no-margin">
<i class="icon-desktop"></i> {% trans "Virtual machines" %} <i class="icon-desktop"></i> {% trans "Virtual machines" %}
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
<div class="list-group" id="vm-list-view"> <div class="list-group" id="vm-list-view">
<div id="dashboard-vm-list"> <div id="dashboard-vm-list">
{% for i in instances %} {% for i in instances %}
<a href="{{ i.get_absolute_url }}" class="list-group-item"> <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"> <span class="index-vm-list-name">
<i class="{{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i> <i class="{{ i.get_status_icon }}" title="{{ i.get_status_display }}"></i>
{{ i.name }} {{ i.name }}
...@@ -30,29 +31,47 @@ ...@@ -30,29 +31,47 @@
</div> </div>
<div style="clear: both;"></div> <div style="clear: both;"></div>
</a> </a>
{% empty %}
<div class="list-group-item list-group-item-last">
{% trans "You have no virtual machines." %}
</div>
{% endfor %} {% endfor %}
</div> </div>
<style>
.list-group-item-last {
border-bottom: 1px solid #ddd !important;
}
</style>
<div href="#" class="list-group-item list-group-footer"> <div href="#" class="list-group-item list-group-footer">
<div class="row"> <div class="row">
<div class="col-sm-6 col-xs-6 input-group input-group-sm"> <div class="col-sm-6 col-xs-6 input-group input-group-sm">
<input id="dashboard-vm-search-input" type="text" class="form-control" placeholder="{% trans "Search..." %}" /> <input id="dashboard-vm-search-input" type="text" class="form-control" placeholder="{% trans "Search..." %}" />
<div class="input-group-btn"> <div class="input-group-btn">
<button type="submit" class="form-control btn btn-primary" title="search"><i class="icon-search"></i></button> <button type="submit" class="form-control btn btn-primary"><i class="icon-search"></i></button>
</div> </div>
</div> </div>
<div class="col-sm-6 text-right"> <div class="col-sm-6 text-right">
{% if more_instances > 0 %}
<a class="btn btn-primary btn-xs" href="{% url "dashboard.views.vm-list" %}"> <a class="btn btn-primary btn-xs" href="{% url "dashboard.views.vm-list" %}">
<i class="icon-chevron-sign-right"></i> {% blocktrans with count=more_instances %}<strong>{{ count }}</strong> more{% endblocktrans %} <i class="icon-chevron-sign-right"></i>
{% if more_instances > 0 %}
{% blocktrans count counter=more_instances %}
<strong>{{ counter }}</strong> more
{% plural %}
<strong>{{ counter }}</strong> more
{% endblocktrans %}
{% else %}
{% trans "list" %}
{% endif %}
</a> </a>
{% endif %}
<a class="btn btn-success btn-xs vm-create" href="{% url "dashboard.views.vm-create" %}"><i class="icon-plus-sign"></i> {% trans "new" %}</a> <a class="btn btn-success btn-xs vm-create" href="{% url "dashboard.views.vm-create" %}"><i class="icon-plus-sign"></i> {% trans "new" %}</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="panel-body" id="vm-graph-view" style="display: none"> <div class="panel-body" id="vm-graph-view" style="display: none">
<p class="pull-right"> <input class="knob" data-fgColor="chartreuse" data-thickness=".4" data-max="{{ request.user.profile.instance_limit }}" data-width="100" data-height="100" data-readOnly="true" value="{{ instances|length|add:more_instances }}"></p> <p class="pull-right">
<input class="knob" data-fgColor="chartreuse" data-thickness=".4" data-max="{{ request.user.profile.instance_limit }}" data-width="100" data-height="100" data-readOnly="true" value="{{ instances|length|add:more_instances }}">
</p>
<p><span class="bigbig">{% blocktrans with count=running_vm_num %}<big>{{ count }}</big> running{% endblocktrans %}</span> <p><span class="bigbig">{% blocktrans with count=running_vm_num %}<big>{{ count }}</big> running{% endblocktrans %}</span>
<ul class="list-inline" style="max-height: 95px; overflow: hidden;"> <ul class="list-inline" style="max-height: 95px; overflow: hidden;">
{% for vm in running_vms %} {% for vm in running_vms %}
...@@ -69,7 +88,11 @@ ...@@ -69,7 +88,11 @@
<div> <div>
<a style="float: right; margin-top: 17px;" href="{% url "dashboard.views.vm-list" %}" class="btn btn-primary btn-xs"> <a style="float: right; margin-top: 17px;" href="{% url "dashboard.views.vm-list" %}" class="btn btn-primary btn-xs">
<i class="icon-chevron-sign-right"></i> <i class="icon-chevron-sign-right"></i>
<strong>{{ instances|length|add:more_instances }}</strong> machines total {% blocktrans count counter=instances|length|add:more_instances %}
<strong>{{ counter }}</strong> machine total
{% plural %}
<strong>{{ counter }}</strong> machines total
{% endblocktrans %}
</a> </a>
<p class="big text-warning">{% blocktrans with count=stopped_vm_num %}<big>{{ count }}</big> stopped{% endblocktrans %}</p> <p class="big text-warning">{% blocktrans with count=stopped_vm_num %}<big>{{ count }}</big> stopped{% endblocktrans %}</p>
</div> </div>
......
...@@ -1476,6 +1476,49 @@ class GroupDetailTest(LoginMixin, TestCase): ...@@ -1476,6 +1476,49 @@ class GroupDetailTest(LoginMixin, TestCase):
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
class GroupListTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
permlist = Permission.objects.all()
self.u1.user_permissions.add(
filter(lambda element: 'group' in element.name and
'add' in element.name, permlist)[0])
self.u2 = User.objects.create(username='user2')
self.u2.set_password('password')
self.u2.save()
self.g1 = Group.objects.create(name='group1')
self.g1.profile.set_user_level(self.u1, 'owner')
self.g1.save()
self.g2 = Group.objects.create(name='group2')
self.g2.profile.set_user_level(self.u1, 'owner')
self.g2.save()
self.g3 = Group.objects.create(name='group3')
self.g3.profile.set_user_level(self.u1, 'owner')
self.g3.save()
def test_anon_filter(self):
c = Client()
response = c.get('/dashboard/group/list/?s="3"')
self.assertEqual(response.status_code, 302)
def test_permitteduser_filter(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/list/?s="3"')
self.assertEqual(response.status_code, 200)
def tearDown(self):
super(GroupListTest, self).tearDown()
self.u1.delete()
self.u2.delete()
self.g1.delete()
self.g2.delete()
class VmDetailVncTest(LoginMixin, TestCase): class VmDetailVncTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json'] fixtures = ['test-vm-fixture.json', 'node.json']
......
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