Commit 9148d878 by Kálmán Viktor

Merge remote-tracking branch 'origin/master' into feature-indexview-fixes

Conflicts:
	circle/dashboard/static/dashboard/dashboard.css
	circle/dashboard/templates/dashboard/index-groups.html
parents 3af145ab 00e6d851
...@@ -18,17 +18,24 @@ ...@@ -18,17 +18,24 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django import contrib from django import contrib
from django.contrib.auth.admin import UserAdmin from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.auth.models import User from django.contrib.auth.models import User, Group
from dashboard.models import Profile from dashboard.models import Profile, GroupProfile
class ProfileInline(contrib.admin.TabularInline): class ProfileInline(contrib.admin.TabularInline):
model = Profile model = Profile
class GroupProfileInline(contrib.admin.TabularInline):
model = GroupProfile
UserAdmin.inlines = (ProfileInline, ) UserAdmin.inlines = (ProfileInline, )
GroupAdmin.inlines = (GroupProfileInline, )
contrib.admin.site.unregister(User) contrib.admin.site.unregister(User)
contrib.admin.site.register(User, UserAdmin) contrib.admin.site.register(User, UserAdmin)
contrib.admin.site.unregister(Group)
contrib.admin.site.register(Group, GroupAdmin)
...@@ -19,11 +19,11 @@ from __future__ import absolute_import ...@@ -19,11 +19,11 @@ from __future__ import absolute_import
from datetime import timedelta from datetime import timedelta
from django.contrib.auth.models import User
from django.contrib.auth.forms import ( from django.contrib.auth.forms import (
AuthenticationForm, PasswordResetForm, SetPasswordForm, AuthenticationForm, PasswordResetForm, SetPasswordForm,
PasswordChangeForm, PasswordChangeForm,
) )
from django.contrib.auth.models import User, Group
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import ( from crispy_forms.layout import (
...@@ -312,6 +312,55 @@ class VmCustomizeForm(forms.Form): ...@@ -312,6 +312,55 @@ class VmCustomizeForm(forms.Form):
) )
class GroupCreateForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(GroupCreateForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_show_labels = False
self.helper.layout = Layout(
Div(
Div(
AnyTag(
'h4',
HTML(_("Name")),
),
css_class="col-sm-10",
),
css_class="row",
),
Div(
Div(
Field('name', id="group-create-name"),
css_class="col-sm-10",
),
css_class="row",
),
Div( # buttons
Div(
AnyTag( # tip: don't try to use Button class
"button",
AnyTag(
"i",
css_class="icon-play"
),
HTML(" Create"),
css_id="vm-create-submit",
css_class="btn btn-success",
),
css_class="col-sm-5",
),
css_class="row",
),
)
class Meta:
model = Group
fields = ['name', ]
class HostForm(forms.ModelForm): class HostForm(forms.ModelForm):
def setowner(self, user): def setowner(self, user):
......
...@@ -26,7 +26,7 @@ from django.contrib.auth.signals import user_logged_in ...@@ -26,7 +26,7 @@ from django.contrib.auth.signals import user_logged_in
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db.models import ( from django.db.models import (
Model, ForeignKey, OneToOneField, CharField, IntegerField, TextField, Model, ForeignKey, OneToOneField, CharField, IntegerField, TextField,
DateTimeField, DateTimeField, permalink,
) )
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _, override, ugettext from django.utils.translation import ugettext_lazy as _, override, ugettext
...@@ -111,6 +111,11 @@ class GroupProfile(AclBase): ...@@ -111,6 +111,11 @@ class GroupProfile(AclBase):
except cls.DoesNotExist: except cls.DoesNotExist:
return Group.objects.get(name=name) return Group.objects.get(name=name)
@permalink
def get_absolute_url(self):
return ('dashboard.views.group-detail', None,
{'pk': self.group.pk})
def get_or_create_profile(self): def get_or_create_profile(self):
obj, created = GroupProfile.objects.get_or_create(group_id=self.pk) obj, created = GroupProfile.objects.get_or_create(group_id=self.pk)
......
...@@ -611,3 +611,26 @@ footer a, footer a:hover, footer a:visited { ...@@ -611,3 +611,26 @@ footer a, footer a:hover, footer a:visited {
#dashboard-vm-list { #dashboard-vm-list {
min-height: 204px; min-height: 204px;
} }
#group-detail-user-table td:first-child, #group-detail-user-table th: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:last-child {
text-align: center;
width: 100px;
}
#group-detail-perm-header {
margin-top: 25px;
}
textarea[name="list-new-namelist"] {
max-width: 500px;
min-height: 80px;
margin-bottom: 10px;
}
/* 2px border bottom for all bootstrap tables */
.table thead>tr>th {
border-bottom: 1px;
}
...@@ -34,6 +34,22 @@ $(function () { ...@@ -34,6 +34,22 @@ $(function () {
return false; return false;
}); });
$('.group-create').click(function(e) {
$.ajax({
type: 'GET',
url: '/dashboard/group/create/',
success: function(data) {
$('body').append(data);
addSliderMiscs();
$('#create-modal').modal('show');
$('#create-modal').on('hidden.bs.modal', function() {
$('#create-modal').remove();
});
}
});
return false;
});
$('.template-choose').click(function(e) { $('.template-choose').click(function(e) {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
......
/* rename */ /* rename */
$("#group-details-h1-name, .group-details-rename-button").click(function() { $("#group-details-h1-name, .group-details-rename-button").click(function() {
$("#group-details-h1-name").hide(); $("#group-details-h1-name").hide();
...@@ -30,4 +29,38 @@ ...@@ -30,4 +29,38 @@
$(".group-details-help").stop().slideToggle(); $(".group-details-help").stop().slideToggle();
}); });
/* for Node removes buttons */
$('.delete-from-group').click(function() {
var href = $(this).attr('href');
var tr = $(this).closest('tr');
var group = $(this).data('group_pk');
var member = $(this).data('member_pk');
var dir = window.location.pathname.indexOf('list') == -1;
addModalConfirmation(removeMember,
{ 'url': href,
'data': [],
'tr': tr,
'group_pk': group,
'member_pk': member,
'type': "user",
'redirect': dir});
return false;
});
function removeMember(data) {
$.ajax({
type: 'POST',
url: data['url'],
headers: {"X-CSRFToken": getCookie('csrftoken')},
success: function(re, textStatus, xhr) {
data['tr'].fadeOut(function() {
$(this).remove();});
},
error: function(xhr, textStatus, error) {
addMessage('Uh oh :(', 'danger')
}
});
}
...@@ -99,7 +99,7 @@ $(function() { ...@@ -99,7 +99,7 @@ $(function() {
$("#group-list-column-name", row).html( $("#group-list-column-name", row).html(
$("<a/>", { $("<a/>", {
'class': "real-link", 'class': "real-link",
href: "/dashboard/group/" + data['node_pk'] + "/", href: "/dashboard/group/" + data['group_pk'] + "/",
text: data['new_name'] text: data['new_name']
}) })
).show(); ).show();
......
...@@ -134,16 +134,19 @@ class GroupListTable(Table): ...@@ -134,16 +134,19 @@ class GroupListTable(Table):
) )
number_of_users = TemplateColumn( number_of_users = TemplateColumn(
orderable=False,
template_name='dashboard/group-list/column-users.html', template_name='dashboard/group-list/column-users.html',
attrs={'th': {'class': 'group-list-table-admin'}}, attrs={'th': {'class': 'group-list-table-admin'}},
) )
admin = TemplateColumn( admin = TemplateColumn(
orderable=False,
template_name='dashboard/group-list/column-admin.html', template_name='dashboard/group-list/column-admin.html',
attrs={'th': {'class': 'group-list-table-admin'}}, attrs={'th': {'class': 'group-list-table-admin'}},
) )
actions = TemplateColumn( actions = TemplateColumn(
orderable=False,
attrs={'th': {'class': 'group-list-table-thin'}}, attrs={'th': {'class': 'group-list-table-thin'}},
template_code=('{% include "dashboard/group-list/column-' template_code=('{% include "dashboard/group-list/column-'
'actions.html" with btn_size="btn-xs" %}'), 'actions.html" with btn_size="btn-xs" %}'),
......
{% load i18n %}
<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
{% if text %}
{{ text }}
{% else %}
{%blocktrans with object=object%}
Are you sure you want to remove <strong>{{ member }}</strong> from <strong>{{ object }}</strong>?
{%endblocktrans%}
{% endif %}
<br />
<div class="pull-right" style="margin-top: 15px;">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button id="confirmation-modal-button" type="button" class="btn btn-warning">Remove</button>
</div>
<div class="clearfix"></div>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
{% load crispy_forms_tags %}
<style>
.row {
margin-bottom: 15px;
}
</style>
<form method="POST" action="/dashboard/group/create/">
{% csrf_token %}
{% crispy form %}
</form>
...@@ -37,51 +37,92 @@ ...@@ -37,51 +37,92 @@
<div class="row"> <div class="row">
<div class="col-md-12" id="group-detail-pane"> <div class="col-md-12" id="group-detail-pane">
<div class="panel panel-default" id="group-detail-panel"> <div class="panel panel-default" id="group-detail-panel">
<div class="tab-content panel-body"> <div class="tab-content panel-body" id="group-form-body">
<h3>{% trans "User list"|capfirst %}</h3>
<h3>{% trans "User list"|capfirst %}</h3> <form action="" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields"> <table class="table table-striped table-with-form-fields table-bordered" id="group-detail-user-table">
<tbody> <tbody>
<thead><tr><th></th><th>{% trans "Who" %}</th><th></th><th></th></tr></thead> <thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "Remove" %}</th></tr></thead>
{% for i in users %} {% for i in users %}
<tr><td><i class="icon-user"></i></td><td>{{i.username}}</td> <tr>
<td><a data-group-pk="{{ i.pk }}" href="#" class="real-link groupuser-delete btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> <td><i class="icon-user"></i></td><td>{{i.username}}</td>
<td>
<a data-group_pk="{{ group.pk }}" data-member_pk="{{i.pk}}" href="{% url "dashboard.views.remove-user" member_pk=i.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a>
</td>
</tr>
{% endfor %} {% endfor %}
<tr><td><i class="icon-plus"></i></td> <tr>
<td><input type="text" class="form-control" name="perm-new-name" <td><i class="icon-plus"></i></td>
placeholder="{% trans "Name of group or user" %}"></td> <td colspan="2">
<input type="text" class="form-control" name="list-new-name"placeholder="{% trans "Name of user" %}">
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<textarea name="list-new-namelist" class="form-control"
placeholder="{% trans "List of usernames (one per line)." %}"></textarea>
<div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div>
</form>
<h3>{% trans "Permissions"|capfirst %}</h3> <h3 id="group-detail-perm-header">{% trans "Permissions"|capfirst %}</h3>
<form action="{{acl.url}}" method="post">{% csrf_token %} <form action="{{acl.url}}" method="post">{% csrf_token %}
<table class="table table-striped table-with-form-fields"> <table class="table table-striped table-with-form-fields table-bordered" id="group-detail-perm-table">
<thead><tr><th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th></th></tr></thead> <thead>
<tr>
<th></th><th>{% trans "Who" %}</th><th>{% trans "What" %}</th><th>{% trans "Remove" %}</th>
</tr>
</thead>
<tbody> <tbody>
{% for i in acl.users %} {% for i in acl.users %}
<tr><td><i class="icon-user"></i></td><td>{{i.user}}</td> <tr>
<td><select class="form-control" name="perm-u-{{i.user.id}}"> <td><i class="icon-user"></i></td><td>{{i.user}}</td>
<td>
<select class="form-control" name="perm-u-{{i.user.id}}">
{% for id, name in acl.levels %} {% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option> <option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %} {% endfor %}
</select></td> </select>
<td class="user-remove"><a href="#" class="btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td></tr> </td>
<td class="user-remove"><a data-group_pk="{{ group.pk }}" data-member_pk="{{i.user.pk }}" href="{% url "dashboard.views.remove-acluser" member_pk=i.user.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a></td>
</tr>
{% endfor %}
{% for i in acl.groups %}
<tr>
<td><i class="icon-group"></i></td><td>{{ i.group }}</td>
<td>
<select class="form-control" name="perm-g-{{ i.group.pk }}">
{% for id, name in acl.levels %}
<option{%if id = i.level%} selected="selected"{%endif%} value="{{id}}">{{name}}</option>
{% endfor %}
</select>
</td>
<td class="user-remove"><a data-group_pk="{{ i.pk }}"data-member_pk="{{i.group.pk }}" href="{% url "dashboard.views.remove-aclgroup" member_pk=i.group.pk group_pk=group.pk %}" class="real-link delete-from-group btn btn-link btn-xs"><i class="icon-remove"><span class="sr-only">{% trans "remove" %}</span></i></a>
</td>
</tr>
{% endfor %} {% endfor %}
<tr><td><i class="icon-plus"></i></td> <tr>
<td><input type="text" class="form-control" name="perm-new-name" <td><i class="icon-plus"></i></td>
placeholder="{% trans "Name of group or user" %}"></td> <td>
<td><select class="form-control" name="perm-new"> <input type="text" class="form-control" name="perm-new-name"
placeholder="{% trans "Name of group or user" %}">
</td>
<td>
<select class="form-control" name="perm-new">
{% for id, name in acl.levels %} {% for id, name in acl.levels %}
<option value="{{id}}">{{name}}</option> <option value="{{id}}">{{name}}</option>
{% endfor %} {% endfor %}
</select></td><td></td> </select>
</td>
<td></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<textarea class="form-control"></textarea>
<div class="form-actions panel-body"> <div class="form-actions">
<button type="submit" class="btn btn-success">{% trans "Save" %}</button> <button type="submit" class="btn btn-success">{% trans "Save" %}</button>
</div> </div>
</form> </form>
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
{% trans "list" %} {% trans "list" %}
{% endif %} {% endif %}
</a> </a>
<a class="btn btn-success btn-xs group-create" href="#"><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-plus-sign"></i> {% trans "new" %} </a>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
</div> </div>
{% endif %} {% endif %}
{% if perms.group %} {% if perms.auth %}
<div class="col-lg-4 col-sm-6"> <div class="col-lg-4 col-sm-6">
{% include "dashboard/index-groups.html" %} {% include "dashboard/index-groups.html" %}
</div> </div>
......
...@@ -871,6 +871,611 @@ class NodeDetailTest(LoginMixin, TestCase): ...@@ -871,6 +871,611 @@ class NodeDetailTest(LoginMixin, TestCase):
self.assertEqual(node_enabled, not Node.objects.get(pk=1).enabled) self.assertEqual(node_enabled, not Node.objects.get(pk=1).enabled)
class GroupCreateTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
# u0 - user with creating group permissions
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
permlist = Permission.objects.all()
self.u0.user_permissions.add(
filter(lambda element: 'group' in element.name and
'add' in element.name, permlist)[0])
# u1 simple user without permissions
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.save()
def tearDown(self):
super(GroupCreateTest, self).tearDown()
self.g1.delete()
self.u0.delete()
self.u1.delete()
self.us.delete()
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 200)
def test_permitted_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/create/')
self.assertEqual(response.status_code, 403)
def test_anon_group_create(self):
c = Client()
groupnum = Group.objects.count()
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum)
def test_unpermitted_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user1')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), groupnum)
def test_permitted_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user0')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum + 1)
def test_superuser_group_create(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/create/', {'name': 'newgroup'})
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum + 1)
def test_namecollision_group_create(self):
# hint: group1 is in setUp, the tests checks creating group with the
# same name
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/create/', {'name': 'group1'})
self.assertEqual(response.status_code, 200)
self.assertEqual(Group.objects.count(), groupnum)
def test_creator_is_owner_when_group_create(self):
# has owner rights in the group the user who created the group?
c = Client()
self.login(c, 'user0')
c.post('/dashboard/group/create/', {'name': 'newgroup'})
newgroup = Group.objects.get(name='newgroup')
self.assertTrue(newgroup.profile.has_level(self.u0, 'owner'))
class GroupDeleteTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
# u0 - user with creating group permissions
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
permlist = Permission.objects.all()
self.u0.user_permissions.add(
filter(lambda element: 'group' in element.name and
'delete' in element.name, permlist)[0])
# u1 simple user without permissions
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.profile.set_user_level(self.u0, 'owner')
self.g1.save()
def tearDown(self):
super(GroupDeleteTest, self).tearDown()
self.g1.delete()
self.u0.delete()
self.u1.delete()
self.us.delete()
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_permitted_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_anon_group_delete(self):
c = Client()
groupnum = Group.objects.count()
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum)
def test_unpermitted_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user1')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), groupnum)
def test_permitted_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'user0')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum - 1)
def test_superuser_group_delete(self):
c = Client()
groupnum = Group.objects.count()
self.login(c, 'superuser')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), groupnum - 1)
class GroupDetailTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json']
def setUp(self):
Instance.get_remote_queue_name = Mock(return_value='test')
# u0 - owner for group1
self.u0 = User.objects.create(username='user0')
self.u0.set_password('password')
self.u0.save()
self.u1 = User.objects.create(username='user1')
self.u1.set_password('password')
self.u1.save()
self.u2 = User.objects.create(username='user2', is_staff=True)
self.u2.set_password('password')
self.u2.save()
self.u3 = User.objects.create(username='user3')
self.u3.set_password('password')
self.u3.save()
# u4 - removable user for group1
self.u4 = User.objects.create(username='user4')
self.u4.set_password('password')
self.u4.save()
self.us = User.objects.create(username='superuser', is_superuser=True)
self.us.set_password('password')
self.us.save()
self.g1 = Group.objects.create(name='group1')
self.g1.profile.set_user_level(self.u0, 'owner')
self.g1.profile.set_user_level(self.u4, 'operator')
self.g1.user_set.add(self.u4)
self.g1.save()
self.g2 = Group.objects.create(name='group2')
self.g2.save()
self.g3 = Group.objects.create(name='group3')
self.g3.save()
self.g1.profile.set_group_level(self.g3, 'operator')
settings["default_vlangroup"] = 'public'
VlanGroup.objects.create(name='public')
def tearDown(self):
super(GroupDetailTest, self).tearDown()
self.g1.delete()
self.g2.delete()
self.g3.delete()
self.u0.delete()
self.u1.delete()
self.u2.delete()
self.us.delete()
self.u3.delete()
self.u4.delete()
def test_404_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/25555/')
self.assertEqual(response.status_code, 404)
def test_404_user_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/25555/')
self.assertEqual(response.status_code, 404)
def test_anon_group_page(self):
c = Client()
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
def test_superuser_group_page(self):
c = Client()
self.login(c, 'superuser')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_acluser_group_page(self):
c = Client()
self.login(c, 'user0')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_acluser2_group_page(self):
self.g1.profile.set_user_level(self.u1, 'operator')
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_unpermitted_user_group_page(self):
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_user_in_userlist_group_page(self):
self.g1.user_set.add(self.u1)
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
def test_groupmember_group_page(self):
self.g2.user_set.add(self.u1)
self.g1.profile.set_group_level(self.g2, 'owner')
c = Client()
self.login(c, 'user1')
response = c.get('/dashboard/group/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 200)
def test_superuser_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'superuser')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups - 1)
def test_unpermitted_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'user3')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 403)
self.assertEqual(Group.objects.count(), num_of_groups)
def test_acl_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
self.login(c, 'user0')
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups - 1)
def test_anon_group_delete(self):
num_of_groups = Group.objects.count()
c = Client()
response = c.post('/dashboard/group/delete/' + str(self.g1.pk) + '/')
self.assertEqual(response.status_code, 302)
self.assertEqual(Group.objects.count(), num_of_groups)
# add to group
def test_anon_add_user_to_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group,
self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_user_to_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_superuser_add_user_to_group(self):
c = Client()
self.login(c, 'superuser')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group + 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_add_user_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/', {'list-new-name': 'user3'})
self.assertEqual(user_in_group + 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_add_multipleuser_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group + 2, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_add_multipleuser_skip_badname_to_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nnoname\r\nuser2'})
self.assertEqual(user_in_group + 2, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_multipleuser_to_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_anon_add_multipleuser_to_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/',
{'list-new-namelist': 'user1\r\nuser2'})
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_anon_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_acluser_to_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users + 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_add_acluser_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'user3', 'perm-new': 'owner'})
self.assertEqual(acl_users + 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_anon_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user3')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups + 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_add_aclgroup_to_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' +
str(self.g1.pk) + '/acl/',
{'perm-new-name': 'group2', 'perm-new': 'owner'})
self.assertEqual(acl_groups + 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
# remove from group
def test_anon_remove_user_from_group(self):
c = Client()
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group,
self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_user_from_group(self):
c = Client()
self.login(c, 'user3')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group, self.g1.user_set.count())
self.assertEqual(response.status_code, 403)
def test_superuser_remove_user_from_group(self):
c = Client()
self.login(c, 'superuser')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group - 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_permitted_remove_user_from_group(self):
c = Client()
self.login(c, 'user0')
user_in_group = self.g1.user_set.count()
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/user/' + str(self.u4.pk) + '/')
self.assertEqual(user_in_group - 1, self.g1.user_set.count())
self.assertEqual(response.status_code, 302)
def test_anon_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_acluser_from_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'superuser')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users - 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_remove_acluser_from_group(self):
c = Client()
gp = self.g1.profile
self.login(c, 'user0')
acl_users = len(gp.get_users_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/user/' + str(self.u4.pk) + '/')
self.assertEqual(acl_users - 1, len(gp.get_users_with_level()))
self.assertEqual(response.status_code, 302)
def test_anon_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_unpermitted_remove_aclgroup_from_group(self):
c = Client()
self.login(c, 'user3')
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 403)
def test_superuser_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
self.login(c, 'superuser')
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups - 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
def test_permitted_remove_aclgroup_from_group(self):
c = Client()
gp = self.g1.profile
acl_groups = len(gp.get_groups_with_level())
self.login(c, 'user0')
response = c.post('/dashboard/group/' + str(self.g1.pk) +
'/remove/acl/group/' + str(self.g3.pk) + '/')
self.assertEqual(acl_groups - 1, len(gp.get_groups_with_level()))
self.assertEqual(response.status_code, 302)
class VmDetailVncTest(LoginMixin, TestCase): class VmDetailVncTest(LoginMixin, TestCase):
fixtures = ['test-vm-fixture.json', 'node.json'] fixtures = ['test-vm-fixture.json', 'node.json']
......
...@@ -21,7 +21,7 @@ from django.conf.urls import patterns, url, include ...@@ -21,7 +21,7 @@ from django.conf.urls import patterns, url, include
from vm.models import Instance from vm.models import Instance
from .views import ( from .views import (
AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete, AclUpdateView, DiskAddView, FavouriteView, GroupAclUpdateView, GroupDelete,
GroupDetailView, GroupList, GroupUserDelete, IndexView, GroupDetailView, GroupList, IndexView,
InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail, InstanceActivityDetail, LeaseCreate, LeaseDelete, LeaseDetail,
MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete, MyPreferencesView, NodeAddTraitView, NodeCreate, NodeDelete,
NodeDetailView, NodeFlushView, NodeGraphView, NodeList, NodeStatus, NodeDetailView, NodeFlushView, NodeGraphView, NodeList, NodeStatus,
...@@ -30,6 +30,8 @@ from .views import ( ...@@ -30,6 +30,8 @@ from .views import (
TransferOwnershipView, vm_activity, VmCreate, VmDelete, VmDetailView, TransferOwnershipView, vm_activity, VmCreate, VmDelete, VmDetailView,
VmDetailVncTokenView, VmGraphView, VmList, VmMassDelete, VmMigrateView, VmDetailVncTokenView, VmGraphView, VmList, VmMassDelete, VmMigrateView,
VmRenewView, DiskRemoveView, get_disk_download_status, InterfaceDeleteView, VmRenewView, DiskRemoveView, get_disk_download_status, InterfaceDeleteView,
GroupRemoveAclUserView, GroupRemoveAclGroupView, GroupRemoveUserView,
GroupCreate,
TemplateChoose, TemplateChoose,
) )
...@@ -116,9 +118,6 @@ urlpatterns = patterns( ...@@ -116,9 +118,6 @@ urlpatterns = patterns(
name='dashboard.views.group-detail'), name='dashboard.views.group-detail'),
url(r'^group/(?P<pk>\d+)/acl/$', GroupAclUpdateView.as_view(), url(r'^group/(?P<pk>\d+)/acl/$', GroupAclUpdateView.as_view(),
name='dashboard.views.group-acl'), name='dashboard.views.group-acl'),
url(r'^groupuser/delete/(?P<pk>\d+)/$', GroupUserDelete.as_view(),
name="dashboard.views.delete-groupuser"),
url(r'^notifications/$', NotificationView.as_view(), url(r'^notifications/$', NotificationView.as_view(),
name="dashboard.views.notifications"), name="dashboard.views.notifications"),
...@@ -134,4 +133,15 @@ urlpatterns = patterns( ...@@ -134,4 +133,15 @@ urlpatterns = patterns(
url(r'^profile/$', MyPreferencesView.as_view(), url(r'^profile/$', MyPreferencesView.as_view(),
name="dashboard.views.profile"), name="dashboard.views.profile"),
url(r'^group/(?P<group_pk>\d+)/remove/acl/user/(?P<member_pk>\d+)/$',
GroupRemoveAclUserView.as_view(),
name="dashboard.views.remove-acluser"),
url(r'^group/(?P<group_pk>\d+)/remove/acl/group/(?P<member_pk>\d+)/$',
GroupRemoveAclGroupView.as_view(),
name="dashboard.views.remove-aclgroup"),
url(r'^group/(?P<group_pk>\d+)/remove/user/(?P<member_pk>\d+)/$',
GroupRemoveUserView.as_view(),
name="dashboard.views.remove-user"),
url(r'^group/create/$', GroupCreate.as_view(),
name='dashboard.views.group-create'),
) )
...@@ -52,18 +52,21 @@ from braces.views._access import AccessMixin ...@@ -52,18 +52,21 @@ from braces.views._access import AccessMixin
from .forms import ( from .forms import (
CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm, CircleAuthenticationForm, DiskAddForm, HostForm, LeaseForm, MyProfileForm,
NodeForm, TemplateForm, TraitForm, VmCustomizeForm, NodeForm, TemplateForm, TraitForm, VmCustomizeForm, GroupCreateForm,
CirclePasswordChangeForm CirclePasswordChangeForm
) )
from .tables import (NodeListTable, NodeVmListTable,
TemplateListTable, LeaseListTable, GroupListTable,) from .tables import (
NodeListTable, NodeVmListTable, TemplateListTable, LeaseListTable,
GroupListTable,
)
from vm.models import ( from vm.models import (
Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface, Instance, instance_activity, InstanceActivity, InstanceTemplate, Interface,
InterfaceTemplate, Lease, Node, NodeActivity, Trait, InterfaceTemplate, Lease, Node, NodeActivity, Trait,
) )
from storage.models import Disk from storage.models import Disk
from firewall.models import Vlan, Host, Rule from firewall.models import Vlan, Host, Rule
from .models import Favourite, Profile from .models import Favourite, Profile, GroupProfile
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -140,7 +143,9 @@ class IndexView(LoginRequiredMixin, TemplateView): ...@@ -140,7 +143,9 @@ class IndexView(LoginRequiredMixin, TemplateView):
}) })
# groups # groups
groups = Group.objects.all() if user.has_module_perms('auth'):
profiles = GroupProfile.get_objects_with_level('operator', user)
groups = Group.objects.filter(groupprofile__in=profiles)
context.update({ context.update({
'groups': groups[:5], 'groups': groups[:5],
'more_groups': groups.count() - len(groups[:5]), 'more_groups': groups.count() - len(groups[:5]),
...@@ -672,6 +677,7 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView): ...@@ -672,6 +677,7 @@ class NodeDetailView(LoginRequiredMixin, SuperuserRequiredMixin, DetailView):
class GroupDetailView(CheckedDetailView): class GroupDetailView(CheckedDetailView):
template_name = "dashboard/group-detail.html" template_name = "dashboard/group-detail.html"
model = Group model = Group
read_level = 'operator'
def get_has_level(self): def get_has_level(self):
return self.object.profile.has_level return self.object.profile.has_level
...@@ -684,12 +690,45 @@ class GroupDetailView(CheckedDetailView): ...@@ -684,12 +690,45 @@ class GroupDetailView(CheckedDetailView):
return context return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object()
if not self.get_has_level()(request.user, 'operator'):
raise PermissionDenied()
if request.POST.get('new_name'): if request.POST.get('new_name'):
return self.__set_name(request) return self.__set_name(request)
if request.POST.get('list-new-name'):
return self.__add_user(request)
if request.POST.get('list-new-namelist'):
return self.__add_list(request)
if (request.POST.get('list-new-name') is not None) and \
(request.POST.get('list-new-namelist') is not None):
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.get_object().pk}))
def __add_user(self, request):
name = request.POST['list-new-name']
self.__add_username(request, name)
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.object.pk}))
def __add_username(self, request, name):
if not name:
return
try:
entity = User.objects.get(username=name)
self.object.user_set.add(entity)
except User.DoesNotExist:
warning(request, _('User "%s" not found.') % name)
def __add_list(self, request):
if not self.get_has_level()(request.user, 'operator'):
raise PermissionDenied()
userlist = request.POST.get('list-new-namelist').split('\r\n')
for line in userlist:
self.__add_username(request, line)
return redirect(reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.object.pk}))
def __set_name(self, request): def __set_name(self, request):
self.object = self.get_object()
new_name = request.POST.get("new_name") new_name = request.POST.get("new_name")
Group.objects.filter(pk=self.object.pk).update( Group.objects.filter(pk=self.object.pk).update(
**{'name': new_name}) **{'name': new_name})
...@@ -827,27 +866,6 @@ class GroupAclUpdateView(AclUpdateView): ...@@ -827,27 +866,6 @@ class GroupAclUpdateView(AclUpdateView):
else: else:
self.set_levels(request, instance) self.set_levels(request, instance)
self.add_levels(request, instance) self.add_levels(request, instance)
# return redirect(self.profile)
return redirect(reverse("dashboard.views.group-detail",
kwargs=self.kwargs))
def repost(self, request, *args, **kwargs):
group = self.get_object()
if not (group.profile.has_level(request.user, "owner") or
getattr(group.profile, 'owner', None) == request.user):
logger.warning('Tried to set permissions of %s by non-owner %s.',
unicode(group), unicode(request.user))
raise PermissionDenied()
name = request.POST['perm-new-name']
if (User.objects.filter(username=name).count() +
Group.objects.filter(name=name).count() < 1
and len(name) > 0):
warning(request, _('User or group "%s" not found.') % name)
else:
self.set_levels(request, group.profile)
self.add_levels(request, group.profile)
return redirect(reverse("dashboard.views.group-detail", return redirect(reverse("dashboard.views.group-detail",
kwargs=self.kwargs)) kwargs=self.kwargs))
...@@ -1130,43 +1148,87 @@ class NodeList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): ...@@ -1130,43 +1148,87 @@ class NodeList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView):
number_of_VMs=Count('instance_set')).select_related('host') number_of_VMs=Count('instance_set')).select_related('host')
class GroupList(LoginRequiredMixin, SuperuserRequiredMixin, SingleTableView): class GroupList(LoginRequiredMixin, SingleTableView):
template_name = "dashboard/group-list.html" template_name = "dashboard/group-list.html"
model = Group model = Group
table_class = GroupListTable table_class = GroupListTable
table_pagination = False table_pagination = False
def get(self, *args, **kwargs):
if self.request.is_ajax():
groups = [{
'url': reverse("dashboard.views.group-detail",
kwargs={'pk': i.pk}),
'name': i.name} for i in self.queryset]
return HttpResponse(
json.dumps(list(groups)),
content_type="application/json",
)
else:
return super(GroupList, self).get(*args, **kwargs)
class GroupUserDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): def get_queryset(self):
logger.debug('GroupList.get_queryset() called. User: %s',
unicode(self.request.user))
profiles = GroupProfile.get_objects_with_level(
'operator', self.request.user)
groups = Group.objects.filter(groupprofile__in=profiles)
s = self.request.GET.get("s")
if s:
groups = groups.filter(name__icontains=s)
return groups
"""This stuff deletes the group.
""" class GroupRemoveUserView(CheckedDetailView, DeleteView):
model = User model = Group
template_name = "dashboard/confirm/base-delete.html" slug_field = 'pk'
slug_url_kwarg = 'group_pk'
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
def get_context_data(self, **kwargs):
context = super(GroupRemoveUserView, self).get_context_data(**kwargs)
try:
context['member'] = User.objects.get(pk=self.member_pk)
except User.DoesNotExist:
raise Http404()
return context
def get_success_url(self):
next = self.request.POST.get('next')
if next:
return next
else:
return reverse_lazy("dashboard.views.group-detail",
kwargs={'pk': self.get_object().pk})
def get(self, request, member_pk, *args, **kwargs):
self.member_pk = member_pk
return super(GroupRemoveUserView, self).get(request, *args, **kwargs)
def get_template_names(self): def get_template_names(self):
if self.request.is_ajax(): if self.request.is_ajax():
return ['dashboard/confirm/ajax-delete.html'] return ['dashboard/confirm/ajax-remove.html']
else: else:
return ['dashboard/confirm/base-delete.html'] return ['dashboard/confirm/base-remove.html']
def get_context_data(self, **kwargs): def remove_member(self, pk):
# this is redundant now, but if we wanna add more to print container = self.get_object()
# we'll need this container.user_set.remove(User.objects.get(pk=pk))
context = super(GroupUserDelete, self).get_context_data(**kwargs)
return context def get_success_message(self):
return _("Member successfully removed from group!")
# github.com/django/django/blob/master/django/views/generic/edit.py#L245
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
object = self.get_object() object = self.get_object()
if not object.profile.has_level(request.user, 'operator'):
object.delete() raise PermissionDenied()
self.remove_member(kwargs["member_pk"])
success_url = self.get_success_url() success_url = self.get_success_url()
success_message = _("Group successfully deleted!") success_message = self.get_success_message()
if request.is_ajax(): if request.is_ajax():
if request.POST.get('redirect').lower() == "true":
messages.success(request, success_message)
return HttpResponse( return HttpResponse(
json.dumps({'message': success_message}), json.dumps({'message': success_message}),
content_type="application/json", content_type="application/json",
...@@ -1175,20 +1237,45 @@ class GroupUserDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): ...@@ -1175,20 +1237,45 @@ class GroupUserDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
messages.success(request, success_message) messages.success(request, success_message)
return HttpResponseRedirect(success_url) return HttpResponseRedirect(success_url)
def get_success_url(self):
next = self.request.POST.get('next')
if next:
return next
else:
return reverse_lazy('dashboard.index')
class GroupRemoveAclUserView(GroupRemoveUserView):
class GroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): def remove_member(self, pk):
container = self.get_object().profile
container.set_level(User.objects.get(pk=pk), None)
def get_success_message(self):
return _("Acl user successfully removed from group!")
class GroupRemoveAclGroupView(GroupRemoveUserView):
def get_context_data(self, **kwargs):
context = super(GroupRemoveUserView, self).get_context_data(**kwargs)
try:
context['member'] = Group.objects.get(pk=self.member_pk)
except User.DoesNotExist:
raise Http404()
return context
def remove_member(self, pk):
container = self.get_object().profile
container.set_level(Group.objects.get(pk=pk), None)
def get_success_message(self):
return _("Acl group successfully removed from group!")
class GroupDelete(CheckedDetailView, DeleteView):
"""This stuff deletes the group. """This stuff deletes the group.
""" """
model = Group model = Group
template_name = "dashboard/confirm/base-delete.html" template_name = "dashboard/confirm/base-delete.html"
read_level = 'operator'
def get_has_level(self):
return self.object.profile.has_level
def get_template_names(self): def get_template_names(self):
if self.request.is_ajax(): if self.request.is_ajax():
...@@ -1196,16 +1283,11 @@ class GroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView): ...@@ -1196,16 +1283,11 @@ class GroupDelete(LoginRequiredMixin, SuperuserRequiredMixin, DeleteView):
else: else:
return ['dashboard/confirm/base-delete.html'] return ['dashboard/confirm/base-delete.html']
def get_context_data(self, **kwargs):
# this is redundant now, but if we wanna add more to print
# we'll need this
context = super(GroupDelete, self).get_context_data(**kwargs)
return context
# github.com/django/django/blob/master/django/views/generic/edit.py#L245 # github.com/django/django/blob/master/django/views/generic/edit.py#L245
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
object = self.get_object() object = self.get_object()
if not object.profile.has_level(request.user, 'owner'):
raise PermissionDenied()
object.delete() object.delete()
success_url = self.get_success_url() success_url = self.get_success_url()
success_message = _("Group successfully deleted!") success_message = _("Group successfully deleted!")
...@@ -1431,6 +1513,49 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView): ...@@ -1431,6 +1513,49 @@ class NodeCreate(LoginRequiredMixin, SuperuserRequiredMixin, TemplateView):
return redirect(path) return redirect(path)
class GroupCreate(LoginRequiredMixin, TemplateView):
form_class = GroupCreateForm
form = None
def get_template_names(self):
if self.request.is_ajax():
return ['dashboard/modal-wrapper.html']
else:
return ['dashboard/nojs-wrapper.html']
def get(self, request, form=None, *args, **kwargs):
if not request.user.has_module_perms('auth'):
raise PermissionDenied()
if form is None:
form = self.form_class()
context = self.get_context_data(**kwargs)
context.update({
'template': 'dashboard/group-create.html',
'box_title': 'Create a Group',
'form': form,
})
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
if not request.user.has_module_perms('auth'):
raise PermissionDenied()
form = self.form_class(request.POST)
if not form.is_valid():
return self.get(request, form, *args, **kwargs)
form.cleaned_data
savedform = form.save()
savedform.profile.set_level(request.user, 'owner')
messages.success(request, _('Group successfully created!'))
if request.is_ajax():
return HttpResponse(json.dumps({'redirect':
savedform.profile.get_absolute_url()}),
content_type="application/json")
else:
return redirect(savedform.profile.get_absolute_url())
class VmDelete(LoginRequiredMixin, DeleteView): class VmDelete(LoginRequiredMixin, DeleteView):
model = Instance model = Instance
template_name = "dashboard/confirm/base-delete.html" template_name = "dashboard/confirm/base-delete.html"
......
This source diff could not be displayed because it is too large. You can view the blob instead.
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#, fuzzy # , 2014.
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-04-16 08:59+0200\n" "POT-Creation-Date: 2014-05-07 14:25+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: 2014-05-07 15:32+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: Mate Ory <orymate@ik.bme.hu>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: Hungarian <cloud@ik.bme.hu>\n"
"Language: \n" "Language: en_US\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.5\n"
#: dashboard/static/dashboard/vm-tour.js:23 #: dashboard/static/dashboard/dashboard.js:54
msgid "Select an option to proceed!"
msgstr "Válasszon a folytatáshoz."
#: dashboard/static/dashboard/vm-tour.js:20
msgid "Prev" msgid "Prev"
msgstr "Előző" msgstr "Vissza"
#: dashboard/static/dashboard/vm-tour.js:25 #: dashboard/static/dashboard/vm-tour.js:22
msgid "Next" msgid "Next"
msgstr "Következő" msgstr "Tovább"
#: dashboard/static/dashboard/vm-tour.js:29 #: dashboard/static/dashboard/vm-tour.js:26
msgid "End tour" msgid "End tour"
msgstr "" msgstr "Befejezés"
#: dashboard/static/dashboard/vm-tour.js:36 #: dashboard/static/dashboard/vm-tour.js:33
msgid "Template Tutorial Tour" msgid "Template Tutorial Tour"
msgstr "" msgstr "Sablon-kalauz"
#: dashboard/static/dashboard/vm-tour.js:37 #: dashboard/static/dashboard/vm-tour.js:34
msgid "" msgid ""
"Welcome to the template tutorial. In this quick tour, we gonna show you how " "Welcome to the template tutorial. In this quick tour, we gonna show you how "
"to do the steps described above." "to do the steps described above."
msgstr "" msgstr ""
"Üdvözöli a sablon-kalauz. A túra során bemutatjuk, hogyan végezze el "
"a fenti lépéseket."
#: dashboard/static/dashboard/vm-tour.js:38 #: dashboard/static/dashboard/vm-tour.js:35
msgid "" msgid ""
"For the next tour step press the \"Next\" button or the right arrow (or " "For the next tour step press the \"Next\" button or the right arrow (or "
"\"Back\" button/left arrow for the previous step)." "\"Back\" button/left arrow for the previous step)."
msgstr "" msgstr ""
"A következő lépéshez kattintson a \"Tovább\" gombra vagy használja "
"a nyílbillentyűket."
#: dashboard/static/dashboard/vm-tour.js:39 #: dashboard/static/dashboard/vm-tour.js:36
msgid "" msgid ""
"During the tour please don't try the functions because it may lead to " "During the tour please don't try the functions because it may lead to "
"graphical glitches, however " "graphical glitches, however "
msgstr "" msgstr "A túra során még ne próbálja ki a bemutatott funkciókat."
#: dashboard/static/dashboard/vm-tour.js:48 #: dashboard/static/dashboard/vm-tour.js:45
msgid "Home tab" msgid "Home tab"
msgstr "" msgstr "Kezdőoldal"
#: dashboard/static/dashboard/vm-tour.js:49 #: dashboard/static/dashboard/vm-tour.js:46
msgid "" msgid ""
"In this tab you can tag your virtual machine and modify the description." "In this tab you can tag your virtual machine and modify the name and "
"description."
msgstr "" msgstr ""
"Ezen a lapon címkéket adhat a virtuális géphez, vagy módosíthatja "
"a nevét, leírását."
#: dashboard/static/dashboard/vm-tour.js:58 #: dashboard/static/dashboard/vm-tour.js:55
msgid "Resources tab" msgid "Resources tab"
msgstr "" msgstr "Erőforrások lap"
#: dashboard/static/dashboard/vm-tour.js:61 #: dashboard/static/dashboard/vm-tour.js:58
msgid "" msgid ""
"On the resources tab you can edit the CPU/RAM options and add/remove disks!" "On the resources tab you can edit the CPU/RAM options and add/remove disks!"
msgstr "" msgstr ""
"Az erőforrások lapon szerkesztheti a CPU/memória-beállításokat, valamint "
"hozzáadhat "
"és törölhet lemezeket."
#: dashboard/static/dashboard/vm-tour.js:71 #: dashboard/static/dashboard/vm-tour.js:68
msgid "Resources" msgid "Resources"
msgstr "" msgstr "Erőforrások"
#: dashboard/static/dashboard/vm-tour.js:72 #: dashboard/static/dashboard/vm-tour.js:69
msgid "CPU priority" msgid "CPU priority"
msgstr "" msgstr "CPU prioritás"
#: dashboard/static/dashboard/vm-tour.js:72 #: dashboard/static/dashboard/vm-tour.js:69
msgid "higher (or lower?) is better" msgid "higher is better"
msgstr "" msgstr "a nagyobb érték a jobb"
#: dashboard/static/dashboard/vm-tour.js:73 #: dashboard/static/dashboard/vm-tour.js:70
msgid "CPU count" msgid "CPU count"
msgstr "" msgstr "CPU-k száma"
#: dashboard/static/dashboard/vm-tour.js:73 #: dashboard/static/dashboard/vm-tour.js:70
msgid "number of CPU cores." msgid "number of CPU cores."
msgstr "" msgstr "A CPU-magok száma."
#: dashboard/static/dashboard/vm-tour.js:74 #: dashboard/static/dashboard/vm-tour.js:71
msgid "RAM amount" msgid "RAM amount"
msgstr "" msgstr "RAM mennyiség"
#: dashboard/static/dashboard/vm-tour.js:74 #: dashboard/static/dashboard/vm-tour.js:71
msgid "amount of RAM." msgid "amount of RAM."
msgstr "" msgstr "a memória mennyisége."
#: dashboard/static/dashboard/vm-tour.js:84 #: dashboard/static/dashboard/vm-tour.js:81
msgid "Disks" msgid "Disks"
msgstr "" msgstr "Lemezek"
#: dashboard/static/dashboard/vm-tour.js:85 #: dashboard/static/dashboard/vm-tour.js:82
msgid "" msgid ""
"You can add empty disks, download new ones and remove existing ones here." "You can add empty disks, download new ones and remove existing ones here."
msgstr "" msgstr ""
"Hozzáadhat üres lemezeket, letölthet lemezképeket, vagy törölheti a "
"meglévőket."
#: dashboard/static/dashboard/vm-tour.js:95 #: dashboard/static/dashboard/vm-tour.js:92
msgid "Network tab" msgid "Network tab"
msgstr "" msgstr "Hálózat lap"
#: dashboard/static/dashboard/vm-tour.js:96 #: dashboard/static/dashboard/vm-tour.js:93
msgid "You can add new network interfaces or remove existing ones here." msgid "You can add new network interfaces or remove existing ones here."
msgstr "" msgstr "Hozzáadhat új hálózati interfészeket, vagy törölheti a meglévőket."
#: dashboard/static/dashboard/vm-tour.js:105 #: dashboard/static/dashboard/vm-tour.js:102
msgid "Deploy" msgid "Deploy"
msgstr "" msgstr "Indítás"
#: dashboard/static/dashboard/vm-tour.js:108 #: dashboard/static/dashboard/vm-tour.js:105
msgid "Deploy the virtual machine." msgid "Deploy the virtual machine."
msgstr "" msgstr "A virtuális gép elindítása."
#: dashboard/static/dashboard/vm-tour.js:113 #: dashboard/static/dashboard/vm-tour.js:110
msgid "Connect" msgid "Connect"
msgstr "" msgstr "Csatlakozás"
#: dashboard/static/dashboard/vm-tour.js:116 #: dashboard/static/dashboard/vm-tour.js:113
msgid "Use the connection string or connect with your choice of client!" msgid "Use the connection string or connect with your choice of client!"
msgstr "" msgstr "Használja a megadott parancsot, vagy kedvenc kliensét."
#: dashboard/static/dashboard/vm-tour.js:123 #: dashboard/static/dashboard/vm-tour.js:120
msgid "Customize the virtual machine" msgid "Customize the virtual machine"
msgstr "" msgstr "Szabja testre a gépet"
#: dashboard/static/dashboard/vm-tour.js:124 #: dashboard/static/dashboard/vm-tour.js:121
msgid "After you have connected to the virtual do you modifications." msgid ""
"After you have connected to the virtual machine do your modifications then "
"log off."
msgstr "" msgstr ""
"Miután csatlakozott, végezze el a szükséges módosításokat, majd "
"jelentkezzen ki."
#: dashboard/static/dashboard/vm-tour.js:129 #: dashboard/static/dashboard/vm-tour.js:126
msgid "Save as" msgid "Save as"
msgstr "" msgstr "Mentés sablonként"
#: dashboard/static/dashboard/vm-tour.js:132 #: dashboard/static/dashboard/vm-tour.js:129
msgid "" msgid ""
"Press the \"Save as template\" button and wait until the activity finishes." "Press the \"Save as template\" button and wait until the activity finishes."
msgstr "" msgstr ""
"Kattintson a „mentés sablonként” gombra, majd várjon, amíg a lemez "
"mentése elkészül."
#: dashboard/static/dashboard/vm-tour.js:138 #: dashboard/static/dashboard/vm-tour.js:135
msgid "Finisih" msgid "Finish"
msgstr "" msgstr "Befejezés"
#: dashboard/static/dashboard/vm-tour.js:141 #: dashboard/static/dashboard/vm-tour.js:138
msgid "" msgid ""
"This is the last message, if something is not clear you can do the the tour " "This is the last message, if something is not clear you can do the the tour "
"again!" "again!"
msgstr "" msgstr ""
"A túra véget ért. Ha valami nem érthető, újrakezdheti az "
"útmutatót."
#: network/static/js/host.js:10 #: network/static/js/host.js:10
msgid "" msgid ""
"Are you sure you want to remove host group <strong>\"%(group)s\"</strong> " "Are you sure you want to remove host group <strong>\"%(group)s\"</strong> "
"from <strong>\"%(host)s\"</strong>?" "from <strong>\"%(host)s\"</strong>?"
msgstr "" msgstr ""
"Biztosan törli a(z)<strong>„%(host)s”</strong> gépet a(z) "
"<strong>„%(group)s”</strong> gépcsoportból?"
#: network/static/js/host.js:13 #: network/static/js/host.js:13
msgid "Are you sure you want to delete this rule?" msgid "Are you sure you want to delete this rule?"
msgstr "" msgstr "Biztosan törli ezt a szabályt?"
#: network/static/js/host.js:20 network/static/js/switch-port.js:14 #: network/static/js/host.js:20 network/static/js/switch-port.js:14
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr "Mégsem"
#: network/static/js/host.js:25 network/static/js/switch-port.js:19 #: network/static/js/host.js:25 network/static/js/switch-port.js:19
msgid "Remove" msgid "Remove"
msgstr "" msgstr "Eltávolítás"
#: network/static/js/switch-port.js:8 #: network/static/js/switch-port.js:8
msgid "Are you sure you want to delete this device?" msgid "Are you sure you want to delete this device?"
msgstr "" msgstr "Biztosan törli ezt az eszközt?"
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