diff --git a/circle/circle/settings/base.py b/circle/circle/settings/base.py index f7d5c49..12c848d 100644 --- a/circle/circle/settings/base.py +++ b/circle/circle/settings/base.py @@ -260,6 +260,7 @@ THIRD_PARTY_APPS = ( 'taggit', 'statici18n', 'django_sshkey', + 'autocomplete_light', ) # Apps specific for this project go here. diff --git a/circle/dashboard/autocomplete_light_registry.py b/circle/dashboard/autocomplete_light_registry.py new file mode 100644 index 0000000..20f0e61 --- /dev/null +++ b/circle/dashboard/autocomplete_light_registry.py @@ -0,0 +1,21 @@ +import autocomplete_light +from django.utils.translation import ugettext as _ + +from .views import AclUpdateView + + +class AclUserAutocomplete(autocomplete_light.AutocompleteGenericBase): + search_fields = ( + ('^first_name', 'last_name', 'username', '^email', 'profile__org_id'), + ('^name', 'groupprofile__org_id'), + ) + autocomplete_js_attributes = {'placeholder': _("Name of group or user")} + + def choices_for_request(self): + user = self.request.user + self.choices = (AclUpdateView.get_allowed_users(user), + AclUpdateView.get_allowed_groups(user)) + return super(AclUserAutocomplete, self).choices_for_request() + + +autocomplete_light.register(AclUserAutocomplete) diff --git a/circle/dashboard/forms.py b/circle/dashboard/forms.py index 3171120..e97b268 100644 --- a/circle/dashboard/forms.py +++ b/circle/dashboard/forms.py @@ -27,6 +27,7 @@ from django.contrib.auth.models import User, Group from django.core.validators import URLValidator from django.core.exceptions import PermissionDenied, ValidationError +import autocomplete_light from crispy_forms.helper import FormHelper from crispy_forms.layout import ( Layout, Div, BaseInput, Field, HTML, Submit, Fieldset, TEMPLATE_PACK, @@ -1185,6 +1186,11 @@ class UserCreationForm(OrgUserCreationForm): return user +class AclUserAddForm(forms.Form): + name = forms.CharField(widget=autocomplete_light.TextWidget( + 'AclUserAutocomplete', attrs={'class': 'form-control'})) + + class UserKeyForm(forms.ModelForm): name = forms.CharField(required=True, label=_('Name')) key = forms.CharField( diff --git a/circle/dashboard/models.py b/circle/dashboard/models.py index 9741a83..6d21943 100644 --- a/circle/dashboard/models.py +++ b/circle/dashboard/models.py @@ -142,6 +142,11 @@ class Profile(Model): def __unicode__(self): return self.get_display_name() + class Meta: + permissions = ( + ('use_autocomplete', _('Can use autocomplete.')), + ) + class FutureMember(Model): org_id = CharField(max_length=64, help_text=_( diff --git a/circle/dashboard/templates/base.html b/circle/dashboard/templates/base.html index 229d621..2061471 100644 --- a/circle/dashboard/templates/base.html +++ b/circle/dashboard/templates/base.html @@ -70,6 +70,7 @@ <script src="//code.jquery.com/jquery-1.11.1.min.js"></script> <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script> <script src="{{ STATIC_URL }}jsi18n/{{ LANGUAGE_CODE }}/djangojs.js"></script> + {% include 'autocomplete_light/static.html' %} {% block extra_script %} {% endblock %} diff --git a/circle/dashboard/templates/dashboard/template-edit.html b/circle/dashboard/templates/dashboard/template-edit.html index 385ef06..7ac6d6f 100644 --- a/circle/dashboard/templates/dashboard/template-edit.html +++ b/circle/dashboard/templates/dashboard/template-edit.html @@ -88,11 +88,12 @@ </tr> {% endfor %} <tr><td><i class="fa fa-plus"></i></td> - <td><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.allowed_levels %} + <td>{{aclform.name }}</td> + <td><select class="form-control" name="level"> + {% for id, name in acl.levels %} + {% if id in acl.allowed_levels %} <option value="{{id}}">{{name}}</option> + {% endif %} {% endfor %} </select></td><td></td> </tr> diff --git a/circle/dashboard/urls.py b/circle/dashboard/urls.py index 8839175..10470a8 100644 --- a/circle/dashboard/urls.py +++ b/circle/dashboard/urls.py @@ -18,6 +18,7 @@ from __future__ import absolute_import from django.conf.urls import patterns, url, include +import autocomplete_light from vm.models import Instance from .views import ( AclUpdateView, FavouriteView, GroupAclUpdateView, GroupDelete, @@ -43,7 +44,10 @@ from .views import ( LeaseAclUpdateView, ) +autocomplete_light.autodiscover() + urlpatterns = patterns( + '', url(r'^$', IndexView.as_view(), name="dashboard.index"), url(r'^lease/(?P<pk>\d+)/$', LeaseDetail.as_view(), @@ -181,4 +185,5 @@ urlpatterns = patterns( url(r'^sshkey/create/$', UserKeyCreate.as_view(), name="dashboard.views.userkey-create"), + url(r'^autocomplete/', include('autocomplete_light.urls')), ) diff --git a/circle/dashboard/views.py b/circle/dashboard/views.py index df6c22d..b5d6e0a 100644 --- a/circle/dashboard/views.py +++ b/circle/dashboard/views.py @@ -61,7 +61,7 @@ from .forms import ( UserCreationForm, GroupProfileUpdateForm, UnsubscribeForm, VmSaveForm, UserKeyForm, VmRenewForm, CirclePasswordChangeForm, VmCreateDiskForm, VmDownloadDiskForm, - TraitsForm, RawDataForm, GroupPermissionForm + TraitsForm, RawDataForm, GroupPermissionForm, AclUserAddForm ) from .tables import ( @@ -1068,8 +1068,8 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): if cls.has_next_level(user, obj, l[0])) is_owner = 'owner' in allowed_levels - allowed_users = cls.get_allowed_users(user, is_owner) - allowed_groups = cls.get_allowed_groups(user, is_owner) + allowed_users = cls.get_allowed_users(user) + allowed_groups = cls.get_allowed_groups(user) user_levels = list( {'user': u, 'level': l} for u, l in obj.get_users_with_level() @@ -1100,30 +1100,28 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): return instance.has_level(user, next_level) @classmethod - def get_allowed_groups(cls, user, is_owner): - if is_owner: + def get_allowed_groups(cls, user): + if user.has_perm('dashboard.use_autocomplete'): return Group.objects.all() else: profiles = GroupProfile.get_objects_with_level('owner', user) return Group.objects.filter(groupprofile__in=profiles).distinct() @classmethod - def get_allowed_users(cls, user, is_owner): - if is_owner: + def get_allowed_users(cls, user): + if user.has_perm('dashboard.use_autocomplete'): return User.objects.all() else: - groups = cls.get_allowed_groups(user, is_owner) + groups = cls.get_allowed_groups(user) return User.objects.filter( Q(groups__in=groups) | Q(pk=user.pk)).distinct() def check_auth(self, whom, old_level, new_level): if isinstance(whom, Group): - if whom not in AclUpdateView.get_allowed_groups(self.request.user, - self.is_owner): + if whom not in AclUpdateView.get_allowed_groups(self.request.user): return False elif isinstance(whom, User): - if whom not in AclUpdateView.get_allowed_users(self.request.user, - self.is_owner): + if whom not in AclUpdateView.get_allowed_users(self.request.user): return False return ( AclUpdateView.has_next_level(self.request.user, @@ -1168,9 +1166,9 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): self.set_level(entity, value) def add_levels(self): - name = self.request.POST.get('perm-new-name', None) - value = self.request.POST.get('perm-new', None) - if not name or not value: + name = self.request.POST.get('name', None) + level = self.request.POST.get('level', None) + if not name or not level: return try: entity = search_user(name) @@ -1182,7 +1180,7 @@ class AclUpdateView(LoginRequiredMixin, View, SingleObjectMixin): messages.warning( self.request, _('User or group "%s" not found.') % name) return - self.set_level(entity, value) + self.set_level(entity, level) def post(self, request, *args, **kwargs): self.instance = self.get_object() @@ -1352,6 +1350,7 @@ class TemplateDetail(LoginRequiredMixin, SuccessMessageMixin, UpdateView): obj, self.request.user, 'dashboard.views.template-acl') context['disks'] = obj.disks.all() context['is_owner'] = obj.has_level(self.request.user, 'owner') + context['aclform'] = AclUserAddForm() return context def get_success_url(self): diff --git a/requirements/base.txt b/requirements/base.txt index 62bdcd5..98d4c6f 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -4,6 +4,7 @@ billiard==3.3.0.17 bpython==0.12 celery==3.1.11 Django==1.6.3 +django-autocomplete-light==1.4.14 django-braces==1.4.0 django-celery==3.1.10 django-crispy-forms==1.4.0