Browse Source

Permission providers, forms, algebra and some docs.

Rafał Pitoń 11 years ago
parent
commit
93ce4ec632

+ 21 - 0
misago/acl/__init__.py

@@ -0,0 +1,21 @@
+from misago.acl.providers import providers  # noqa
+
+
+def get_change_permissions_forms(role):
+    return providers.get_change_permissions_forms(role)
+
+
+def build_acl_cache(roles):
+    return providers.build_acl_cache(roles)
+
+
+def hydrate_acl_cache(acl):
+    return providers.hydrate_acl_cache(acl)
+
+
+def get_acl_by_token(token):
+    return providers.get_acl_by_token(token)
+
+
+def get_acl_by_roles(roles):
+    return providers.get_acl_by_roles(roles)

+ 10 - 34
misago/acl/models.py

@@ -9,21 +9,24 @@ except ImportError:
     import pickle
 
 
-class Role(models.Model):
+class BaseRole(models.Model):
     name = models.CharField(max_length=255)
     pickled_permissions = models.TextField(null=True, blank=True)
 
+    class Meta:
+        abstract = True
+
     def __unicode__(self):
         return unicode(_(self.name))
 
     def save(self, *args, **kwargs):
         if self.pk:
             cachebuster.invalidate()
-        return super(Role, self).save(*args, **kwargs)
+        return super(BaseRole, self).save(*args, **kwargs)
 
     def delete(self, *args, **kwargs):
         cachebuster.invalidate()
-        return super(Role, self).delete(*args, **kwargs)
+        return super(BaseRole, self).delete(*args, **kwargs)
 
     @property
     def permissions(self):
@@ -44,39 +47,12 @@ class Role(models.Model):
             pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
 
 
-class ForumRole(models.Model):
-    name = models.CharField(max_length=255)
-    pickled_permissions = models.TextField(null=True, blank=True)
+class Role(BaseRole):
+    pass
 
-    def __unicode__(self):
-        return unicode(_(self.name))
 
-    def save(self, *args, **kwargs):
-        if self.pk:
-            cachebuster.invalidate()
-        return super(ForumRole, self).save(*args, **kwargs)
-
-    def delete(self, *args, **kwargs):
-        cachebuster.invalidate()
-        return super(ForumRole, self).delete(*args, **kwargs)
-
-    @property
-    def permissions(self):
-        try:
-            return self.permissions_cache
-        except AttributeError:
-            try:
-                self.permissions_cache = pickle.loads(
-                    base64.decodestring(self.pickled_permissions))
-            except Exception:
-                self.permissions_cache = {}
-        return self.permissions_cache
-
-    @permissions.setter
-    def permissions(self, permissions):
-        self.permissions_cache = permissions
-        self.pickled_permissions = base64.encodestring(
-            pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
+class ForumRole(BaseRole):
+    pass
 
 
 """register models in misago admin"""

+ 49 - 0
misago/acl/providers.py

@@ -0,0 +1,49 @@
+from importlib import import_module
+from django.conf import settings
+
+
+class PermissionProviders(object):
+    def __init__(self):
+        self._initialized = False
+        self._providers = []
+        self._providers_dict = {}
+
+    def initialize_providers(self):
+        if not self._initialized:
+            self.import_providers()
+            self._initialized = True
+
+    def import_providers(self):
+        for namespace in settings.MISAGO_PERMISSION_PROVIDERS:
+            self._providers.append((namespace, import_module(namespace)))
+            self._providers_dict[namespace] = import_module(namespace)
+
+    def get_change_permissions_forms(self, role):
+        self.initialize_providers()
+
+        forms = []
+        for provider, module in self._providers:
+            try:
+                module.change_permissions_form
+            except AttributeError:
+                message = "'%s' object has no attribute '%s'"
+                raise AttributeError(
+                    message % (provider, 'change_permissions_form'))
+            forms.append(module.change_permissions_form(role))
+
+        return [f for f in forms if f]
+
+    def build_acl_cache(self, roles):
+        self.initialize_providers()
+
+    def hydrate_acl_cache(self, acl):
+        self.initialize_providers()
+
+    def get_acl_by_token(self, token):
+        pass
+
+    def get_acl_by_roles(self, roles):
+        pass
+
+
+providers = PermissionProviders()

+ 2 - 0
misago/acl/utils.py

@@ -0,0 +1,2 @@
+def make_acl_token(roles):
+    pass

+ 45 - 3
misago/acl/views/forumroles.py

@@ -1,6 +1,8 @@
 from django.contrib import messages
+from django.shortcuts import redirect
 from django.utils.translation import ugettext_lazy as _
 from misago.admin.views import generic
+from misago.acl import get_change_permissions_forms
 from misago.acl.models import ForumRole
 from misago.acl.forms import ForumRoleForm
 
@@ -8,7 +10,6 @@ from misago.acl.forms import ForumRoleForm
 class ForumRoleAdmin(generic.AdminBaseMixin):
     root_link = 'misago:admin:permissions:forums:index'
     Model = ForumRole
-    Form = ForumRoleForm
     templates_dir = 'misago/admin/forumroles'
     message_404 = _("Requested role does not exist.")
 
@@ -17,11 +18,52 @@ class ForumRolesList(ForumRoleAdmin, generic.ListView):
     ordering = (('name', None),)
 
 
-class NewForumRole(ForumRoleAdmin, generic.ModelFormView):
+class RoleFormMixin(object):
+    def real_dispatch(self, request, target):
+        role_permissions = target.permissions
+        form = ForumRoleForm(instance=target)
+
+        perms_forms = get_change_permissions_forms(target)
+
+        if request.method == 'POST':
+            perms_forms = [f(request.POST) for f in perms_forms]
+            valid_forms = 0
+            for permissions_form in perms_forms:
+                if permissions_form.is_valid():
+                    valid_forms += 1
+
+            form = ForumRoleForm(request.POST, instance=target)
+            if form.is_valid() and len(perms_forms) == valid_forms:
+                new_permissions = {}
+                for permissions_form in perms_forms:
+                    new_permissions.update(permissions_form.cleaned_data)
+
+                form.instance.permissions = new_permissions
+                form.instance.save()
+
+                messages.success(request, self.message_submit % target.name)
+
+                if 'stay' in request.POST:
+                    return redirect(request.path)
+                else:
+                    return redirect(self.root_link)
+        else:
+            perms_forms = [f(initial=role_permissions) for f in perms_forms]
+
+        return self.render(
+            request,
+            {
+                'form': form,
+                'target': target,
+                'perms_forms': perms_forms,
+            })
+
+
+class NewForumRole(RoleFormMixin, ForumRoleAdmin, generic.ModelFormView):
     message_submit = _('New role "%s" has been saved.')
 
 
-class EditForumRole(ForumRoleAdmin, generic.ModelFormView):
+class EditForumRole(RoleFormMixin, ForumRoleAdmin, generic.ModelFormView):
     message_submit = _('Role "%s" has been changed.')
 
 

+ 45 - 3
misago/acl/views/roles.py

@@ -1,6 +1,8 @@
 from django.contrib import messages
+from django.shortcuts import redirect
 from django.utils.translation import ugettext_lazy as _
 from misago.admin.views import generic
+from misago.acl import get_change_permissions_forms
 from misago.acl.models import Role
 from misago.acl.forms import RoleForm
 
@@ -8,7 +10,6 @@ from misago.acl.forms import RoleForm
 class RoleAdmin(generic.AdminBaseMixin):
     root_link = 'misago:admin:permissions:users:index'
     Model = Role
-    Form = RoleForm
     templates_dir = 'misago/admin/roles'
     message_404 = _("Requested role does not exist.")
 
@@ -17,11 +18,52 @@ class RolesList(RoleAdmin, generic.ListView):
     ordering = (('name', None),)
 
 
-class NewRole(RoleAdmin, generic.ModelFormView):
+class RoleFormMixin(object):
+    def real_dispatch(self, request, target):
+        role_permissions = target.permissions
+        form = RoleForm(instance=target)
+
+        perms_forms = get_change_permissions_forms(target)
+
+        if request.method == 'POST':
+            perms_forms = [f(request.POST) for f in perms_forms]
+            valid_forms = 0
+            for permissions_form in perms_forms:
+                if permissions_form.is_valid():
+                    valid_forms += 1
+
+            form = RoleForm(request.POST, instance=target)
+            if form.is_valid() and len(perms_forms) == valid_forms:
+                new_permissions = {}
+                for permissions_form in perms_forms:
+                    new_permissions.update(permissions_form.cleaned_data)
+
+                form.instance.permissions = new_permissions
+                form.instance.save()
+
+                messages.success(request, self.message_submit % target.name)
+
+                if 'stay' in request.POST:
+                    return redirect(request.path)
+                else:
+                    return redirect(self.root_link)
+        else:
+            perms_forms = [f(initial=role_permissions) for f in perms_forms]
+
+        return self.render(
+            request,
+            {
+                'form': form,
+                'target': target,
+                'perms_forms': perms_forms,
+            })
+
+
+class NewRole(RoleFormMixin, RoleAdmin, generic.ModelFormView):
     message_submit = _('New role "%s" has been saved.')
 
 
-class EditRole(RoleAdmin, generic.ModelFormView):
+class EditRole(RoleFormMixin, RoleAdmin, generic.ModelFormView):
     message_submit = _('Role "%s" has been changed.')
 
 

+ 3 - 0
misago/conf/defaults.py

@@ -127,6 +127,9 @@ TEMPLATE_CONTEXT_PROCESSORS = (
 )
 
 MISAGO_PERMISSION_PROVIDERS = (
+    'misago.users.permissions.account',
+    'misago.users.permissions.profiles',
+    'misago.users.permissions.destroying',
     'misago.forums.permissions',
 )
 

+ 16 - 0
misago/forums/permissions.py

@@ -0,0 +1,16 @@
+from django.utils.translation import ugettext_lazy as _
+from misago.acl.models import ForumRole
+from misago.core import forms
+
+
+class PermissionsForm(forms.Form):
+    legend = _("Destroying user accounts")
+    cam_see_forum = forms.YesNoSwitch(label=_("Can see forum"))
+    can_browse_forum = forms.YesNoSwitch(label=_("Can see forum contents"))
+
+
+def change_permissions_form(role):
+    if role.__class__ == ForumRole:
+        return PermissionsForm
+    else:
+        return None

+ 30 - 5
misago/static/misago/admin/css/misago/forms.less

@@ -81,6 +81,20 @@
 }
 
 
+//== Form alerts
+//
+//**
+.form-panel {
+  .misago-alerts {
+    margin: 0px @form-panel-padding-vertical;
+
+    .alert.lead {
+      font-size: @font-size-large;
+    }
+  }
+}
+
+
 //== Form body
 //
 //**
@@ -139,15 +153,26 @@
 }
 
 
-//== Form alerts
+// Form permissions
 //
 //**
 .form-panel {
-  .misago-alerts {
-    margin: 0px @form-panel-padding-vertical;
+  .table-permissions {
+    margin: 0px;
 
-    .alert.lead {
-      font-size: @font-size-large;
+    tr {
+      label {
+        margin: 0px;
+      }
+
+      p, .form-group {
+        margin-bottom: 0px;
+      }
+
+      td:first-child, th:first-child {
+        padding-left: @form-panel-padding-vertical;
+        vertical-align: middle;
+      }
     }
   }
 }

+ 19 - 4
misago/static/misago/admin/css/style.css

@@ -6583,6 +6583,12 @@ body {
   margin: 0px;
   padding: 4px 0px;
 }
+.form-panel .misago-alerts {
+  margin: 0px 15px;
+}
+.form-panel .misago-alerts .alert.lead {
+  font-size: 18px;
+}
 .form-panel .form-body fieldset,
 .form-panel .form-body.no-fieldsets {
   margin: 0px;
@@ -6641,11 +6647,20 @@ body {
   padding-top: 20px;
   padding-bottom: 5px;
 }
-.form-panel .misago-alerts {
-  margin: 0px 15px;
+.form-panel .table-permissions {
+  margin: 0px;
 }
-.form-panel .misago-alerts .alert.lead {
-  font-size: 18px;
+.form-panel .table-permissions tr label {
+  margin: 0px;
+}
+.form-panel .table-permissions tr p,
+.form-panel .table-permissions tr .form-group {
+  margin-bottom: 0px;
+}
+.form-panel .table-permissions tr td:first-child,
+.form-panel .table-permissions tr th:first-child {
+  padding-left: 15px;
+  vertical-align: middle;
 }
 .form-panel .form-footer {
   background-color: #f7f7f7;

+ 11 - 2
misago/templates/misago/admin/forumroles/form.html

@@ -32,9 +32,18 @@
 
 
 {% block form-body %}
-<div class="form-body no-fieldsets">
+<div class="form-body">
 
-  {{ form.name|as_crispy_field }}
+  <fieldset>
+    <legend>{% trans "Basic settings" %}</legend>
+
+    {{ form.name|as_crispy_field }}
+
+  </fieldset>
+
+  {% for form in perms_forms %}
+  {% include "misago/admin/permissions_table.html" %}
+  {% endfor %}
 
 </div>
 {% endblock form-body %}

+ 33 - 0
misago/templates/misago/admin/permissions_table.html

@@ -0,0 +1,33 @@
+{% load crispy_forms_field crispy_forms_tags i18n %}
+<fieldset>
+  <legend>{{ form.legend }}</legend>
+
+  <table class="table table-striped table-permissions">
+    {% for field in form %}
+    <tr>
+      <td class="col-md-8">
+        <label>{{ field.label }}:</label>
+        {% if field.help_text %}
+        <p class="text-muted small">{{ field.help_text }}</p>
+        {% endif %}
+      </td>
+      <td>
+        <div class="form-group">
+          {% if field|is_checkboxselectmultiple %}
+              {% include 'bootstrap3/layout/checkboxselectmultiple.html' %}
+          {% endif %}
+
+          {% if field|is_radioselect %}
+              {% include 'bootstrap3/layout/radioselect.html' %}
+          {% endif %}
+
+          {% if not field|is_checkboxselectmultiple and not field|is_radioselect %}
+          {% crispy_field field %}
+          {% endif %}
+        </div>
+      </td>
+    </tr>
+    {% endfor %}
+  </table>
+
+</fieldset>

+ 12 - 3
misago/templates/misago/admin/roles/form.html

@@ -1,5 +1,5 @@
 {% extends "misago/admin/generic/form.html" %}
-{% load crispy_forms_filters i18n %}
+{% load crispy_forms_tags i18n %}
 
 
 {% block title %}
@@ -32,9 +32,18 @@
 
 
 {% block form-body %}
-<div class="form-body no-fieldsets">
+<div class="form-body">
 
-  {{ form.name|as_crispy_field }}
+  <fieldset>
+    <legend>{% trans "Basic settings" %}</legend>
+
+    {{ form.name|as_crispy_field }}
+
+  </fieldset>
+
+  {% for form in perms_forms %}
+  {% include "misago/admin/permissions_table.html" %}
+  {% endfor %}
 
 </div>
 {% endblock form-body %}

+ 0 - 0
misago/users/permissions/__init__.py


+ 30 - 0
misago/users/permissions/account.py

@@ -0,0 +1,30 @@
+from django.utils.translation import ugettext_lazy as _
+from misago.acl.models import Role
+from misago.core import forms
+
+
+class PermissionsForm(forms.Form):
+    legend = _("Account settings")
+    name_changes_allowed = forms.IntegerField(
+        label=_("Allowed username changes number"),
+        min_value=0,
+        initial=1)
+    changes_expire = forms.IntegerField(
+        label=_("Don't count username changes older than"),
+        help_text=_("Number of days since name change that makes that change no longer count to limit. Enter zero to make all changes count."),
+        min_value=0,
+        initial=0)
+    can_use_signature = forms.YesNoSwitch(
+        label=_("Can have signature"),
+        initial=False)
+    allow_signature_links = forms.YesNoSwitch(
+        label=_("Can put links in signature"))
+    allow_signature_images = forms.YesNoSwitch(
+        label=_("Can put images in signature"))
+
+
+def change_permissions_form(role):
+    if role.__class__ == Role:
+        return PermissionsForm
+    else:
+        return None

+ 24 - 0
misago/users/permissions/destroying.py

@@ -0,0 +1,24 @@
+from django.utils.translation import ugettext_lazy as _
+from misago.acl.models import Role
+from misago.core import forms
+
+
+class PermissionsForm(forms.Form):
+    legend = _("Destroying user accounts")
+    can_destroy_user_newer_than = forms.IntegerField(
+        label=_("Maximum age of destroyed account (in days)"),
+        help_text=_("Enter zero to disable this check."),
+        initial=0,
+        min_value=0)
+    can_destroy_users_with_less_posts_than = forms.IntegerField(
+        label=_("Maximum number of posts on destroyed account"),
+        help_text=_("Enter zero to disable this check."),
+        initial=0,
+        min_value=0)
+
+
+def change_permissions_form(role):
+    if role.__class__ == Role:
+        return PermissionsForm
+    else:
+        return None

+ 23 - 0
misago/users/permissions/profiles.py

@@ -0,0 +1,23 @@
+from django.utils.translation import ugettext_lazy as _
+from misago.acl.models import Role
+from misago.core import forms
+
+
+class PermissionsForm(forms.Form):
+    legend = _("User profiles")
+    can_search_users = forms.YesNoSwitch(
+        label=_("Can search user profiles"))
+    can_see_users_emails = forms.YesNoSwitch(
+        label=_("Can see members e-mail's"))
+    can_see_users_trails = forms.YesNoSwitch(
+        label=_("Can see members ip's and user-agents"))
+    can_see_hidden_users = forms.YesNoSwitch(
+        label=_("Can see members that hide their presence"))
+
+
+
+def change_permissions_form(role):
+    if role.__class__ == Role:
+        return PermissionsForm
+    else:
+        return None