Browse Source

Admin CRUD for both kinds of roles, simpler urls

Rafał Pitoń 11 years ago
parent
commit
1f62a9083d

+ 1 - 8
docs/developers/admin_actions.rst

@@ -168,14 +168,7 @@ Registering urls under ``misago:admin`` namespace
 Your admin links will live under ``misago:admin`` namespace, which means they have to be registered in it beforehand. Similiarly to Django, Misago uses small discovery routine which discovers modules that are expected to register their urls in admin.
 Your admin links will live under ``misago:admin`` namespace, which means they have to be registered in it beforehand. Similiarly to Django, Misago uses small discovery routine which discovers modules that are expected to register their urls in admin.
 
 
 .. warning::
 .. warning::
-   Presented solution is only temporary. Starting with Django 1.7, you will have to make decision where to locate code that will register your links in Misago admin and call it within your app ``apps.py`` initializer.
-
-Depending on structure of your app and your tastes, this module can be one of following:
-
-* yourapp.adminurls module
-* yourapp.urls.admin module
-
-Each of those is checked, in this order. First one to be found is included in urlconf. Once file is found, code within it its executed with assumption that it will register namespaces and patters in Misago admin.
+   Currently only way to register urls in admin site without ingerention into ``misago.admin`` app is to define your patterns in ``models.py`` . Starting with Django 1.7, you will have to make decision where to locate code that will register your links in Misago admin and call it within your app ``apps.py`` initializer.
 
 
 Admin links are stored within instance of special object :py:class:`misago.admin.urlpatterns.URLPatterns` avaialable under ``misago.admin.urlpatterns``. This object exposes two methods as public api:
 Admin links are stored within instance of special object :py:class:`misago.admin.urlpatterns.URLPatterns` avaialable under ``misago.admin.urlpatterns``. This object exposes two methods as public api:
 
 

+ 27 - 0
misago/acl/adminurls.py

@@ -0,0 +1,27 @@
+from django.conf.urls import url
+from misago.admin import urlpatterns
+from misago.acl.views import roles, forumroles
+
+
+# Users section
+urlpatterns.namespace(r'^permissions/', 'permissions')
+
+
+# Roles
+urlpatterns.namespace(r'^users/', 'users', 'permissions')
+urlpatterns.patterns('permissions:users',
+    url(r'^$', roles.RolesList.as_view(), name='index'),
+    url(r'^new/$', roles.NewRole.as_view(), name='new'),
+    url(r'^edit/(?P<role_id>\d+)/$', roles.EditRole.as_view(), name='edit'),
+    url(r'^delete/(?P<role_id>\d+)/$', roles.DeleteRole.as_view(), name='delete'),
+)
+
+
+# Forum Roles
+urlpatterns.namespace(r'^forums/', 'forums', 'permissions')
+urlpatterns.patterns('permissions:forums',
+    url(r'^$', forumroles.ForumRolesList.as_view(), name='index'),
+    url(r'^new/$', forumroles.NewForumRole.as_view(), name='new'),
+    url(r'^edit/(?P<role_id>\d+)/$', forumroles.EditForumRole.as_view(), name='edit'),
+    url(r'^delete/(?P<role_id>\d+)/$', forumroles.DeleteForumRole.as_view(), name='delete'),
+)

+ 19 - 0
misago/acl/forms.py

@@ -0,0 +1,19 @@
+from django.utils.translation import ugettext_lazy as _
+from misago.core import forms
+from misago.acl.models import Role, ForumRole
+
+
+class RoleForm(forms.ModelForm):
+    name = forms.CharField(label=_("Role name"))
+
+    class Meta:
+        model = Role
+        fields = ['name']
+
+
+class ForumRoleForm(forms.ModelForm):
+    name = forms.CharField(label=_("Role name"))
+
+    class Meta:
+        model = ForumRole
+        fields = ['name']

+ 25 - 0
misago/acl/models.py

@@ -1,5 +1,6 @@
 from django.db import models
 from django.db import models
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
+from misago.admin import site
 import base64
 import base64
 try:
 try:
     import cPickle as pickle
     import cPickle as pickle
@@ -57,3 +58,27 @@ class ForumRole(models.Model):
         self.permissions_cache = permissions
         self.permissions_cache = permissions
         self.pickled_permissions = base64.encodestring(
         self.pickled_permissions = base64.encodestring(
             pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
             pickle.dumps(permissions, pickle.HIGHEST_PROTOCOL))
+
+
+"""register models in misago admin"""
+site.add_node(
+    parent='misago:admin',
+    after='misago:admin:users:accounts:index',
+    namespace='misago:admin:permissions',
+    link='misago:admin:permissions:users:index',
+    name=_("Permissions"),
+    icon='fa fa-adjust')
+
+site.add_node(
+    parent='misago:admin:permissions',
+    namespace='misago:admin:permissions:users',
+    link='misago:admin:permissions:users:index',
+    name=_("User Roles"),
+    icon='fa fa-th-large')
+
+site.add_node(
+    parent='misago:admin:permissions',
+    namespace='misago:admin:permissions:forums',
+    link='misago:admin:permissions:forums:index',
+    name=_("Forum Roles"),
+    icon='fa fa-list')

+ 0 - 0
misago/acl/views/__init__.py


+ 38 - 0
misago/acl/views/forumroles.py

@@ -0,0 +1,38 @@
+from django.contrib import messages
+from django.utils.translation import ugettext_lazy as _
+from misago.admin.views import generic
+from misago.acl.models import ForumRole
+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.")
+
+
+class ForumRolesList(ForumRoleAdmin, generic.ListView):
+    ordering = (('name', None),)
+
+
+class NewForumRole(ForumRoleAdmin, generic.ModelFormView):
+    message_submit = _('New role "%s" has been saved.')
+
+
+class EditForumRole(ForumRoleAdmin, generic.ModelFormView):
+    message_submit = _('Role "%s" has been changed.')
+
+
+class DeleteForumRole(ForumRoleAdmin, generic.ButtonView):
+    def check_permissions(self, request, target):
+        if not target.user_set.exists():
+            message = _('Role "%s" is assigned to forums and '
+                        'can\'t be deleted.')
+            return message % unicode(target.name)
+
+    def button_action(self, request, target):
+        target.delete()
+        message = _('Role "%s" has been deleted.') % unicode(target.name)
+        messages.success(request, message)

+ 38 - 0
misago/acl/views/roles.py

@@ -0,0 +1,38 @@
+from django.contrib import messages
+from django.utils.translation import ugettext_lazy as _
+from misago.admin.views import generic
+from misago.acl.models import Role
+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.")
+
+
+class RolesList(RoleAdmin, generic.ListView):
+    ordering = (('name', None),)
+
+
+class NewRole(RoleAdmin, generic.ModelFormView):
+    message_submit = _('New role "%s" has been saved.')
+
+
+class EditRole(RoleAdmin, generic.ModelFormView):
+    message_submit = _('Role "%s" has been changed.')
+
+
+class DeleteRole(RoleAdmin, generic.ButtonView):
+    def check_permissions(self, request, target):
+        if not target.user_set.exists():
+            message = _('Role "%s" is assigned to users and '
+                        'can\'t be deleted.')
+            return message % unicode(target.name)
+
+    def button_action(self, request, target):
+        target.delete()
+        message = _('Role "%s" has been deleted.') % unicode(target.name)
+        messages.success(request, message)

+ 4 - 16
misago/admin/urls.py

@@ -14,22 +14,10 @@ urlpatterns = patterns('misago.admin.views',
 )
 )
 
 
 
 
-# Magic voodoo for initializing admin patterns lazily
-def initialize_admin_urls():
-    SEARCH_PATTERNS = (
-        '%s.adminurls',
-        '%s.urls.admin',
-        )
-
-    for app in settings.INSTALLED_APPS:
-        for pattern in SEARCH_PATTERNS:
-            try:
-                importlib.import_module(pattern % app)
-                continue
-            except ImportError:
-                pass
-
+# Import admin urls
+import misago.conf.adminurls
+import misago.acl.adminurls
+import misago.users.urls.admin
 
 
 # Register discovered patterns
 # Register discovered patterns
-initialize_admin_urls()
 urlpatterns += admin.urlpatterns()
 urlpatterns += admin.urlpatterns()

+ 1 - 1
misago/admin/views/generic.py

@@ -256,7 +256,7 @@ class ModelFormView(FormView):
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
         form.instance.save()
         form.instance.save()
         if self.message_submit:
         if self.message_submit:
-            messages.success(request, self.message_submit)
+            messages.success(request, self.message_submit % target.name)
 
 
     def real_dispatch(self, request, target):
     def real_dispatch(self, request, target):
         FormType = self.create_form_type(request, target)
         FormType = self.create_form_type(request, target)

+ 1 - 0
misago/conf/defaults.py

@@ -92,6 +92,7 @@ INSTALLED_APPS = (
     'south',
     'south',
     'pipeline',
     'pipeline',
     'crispy_forms',
     'crispy_forms',
+    'misago.admin',
     'misago.acl',
     'misago.acl',
     'misago.core',
     'misago.core',
     'misago.conf',
     'misago.conf',

+ 4 - 0
misago/static/misago/admin/css/misago/tables.less

@@ -98,6 +98,10 @@
             &>span {
             &>span {
               position: relative;
               position: relative;
               bottom: 1px;
               bottom: 1px;
+
+              &.fa-pencil {
+                bottom: -2px;
+              }
             }
             }
           }
           }
         }
         }

+ 5 - 0
misago/static/misago/admin/css/style.css

@@ -6242,6 +6242,11 @@ body {
   position: relative;
   position: relative;
   bottom: 1px;
   bottom: 1px;
 }
 }
+.table-panel table.table tr td.row-action .btn > span.fa-pencil,
+.table-panel table.table tr td.row-action > form .btn > span.fa-pencil,
+.table-panel table.table tr td.row-action .dropdown-toggle > span.fa-pencil {
+  bottom: -2px;
+}
 .table-panel table.table tr td.row-select {
 .table-panel table.table tr td.row-select {
   width: 1%;
   width: 1%;
 }
 }

+ 40 - 0
misago/templates/misago/admin/forumroles/form.html

@@ -0,0 +1,40 @@
+{% extends "misago/admin/generic/form.html" %}
+{% load crispy_forms_filters i18n %}
+
+
+{% block title %}
+{% if target.pk %}
+{% trans target.name %}
+{% else %}
+{% trans "New role" %}
+{% endif %} | {{ active_link.name }} | {{ block.super }}
+{% endblock title %}
+
+
+{% block page-target %}
+{% if target.pk %}
+{% trans target.name %}
+{% else %}
+{% trans "New role" %}
+{% endif %}
+{% endblock page-target %}
+
+
+{% block form-header %}
+<h1>
+  {% if target.pk %}
+  {% trans target.name %}
+  {% else %}
+  {% trans "New role" %}
+  {% endif %}
+</h1>
+{% endblock %}
+
+
+{% block form-body %}
+<div class="form-body no-fieldsets">
+
+  {{ form.name|as_crispy_field }}
+
+</div>
+{% endblock form-body %}

+ 58 - 0
misago/templates/misago/admin/forumroles/list.html

@@ -0,0 +1,58 @@
+{% extends "misago/admin/generic/list.html" %}
+{% load i18n %}
+
+
+{% block page-actions %}
+<div class="page-actions">
+  <a href="{% url 'misago:admin:permissions:forums:new' %}" class="btn btn-success">
+    <span class="fa fa-plus-circle"></span>
+    {% trans "New role" %}
+  </a>
+</div>
+{% endblock %}
+
+
+{% block table-header %}
+<th>{% trans "Rank" %}</th>
+<th style="width: 1%;">&nbsp;</th>
+<th style="width: 1%;">&nbsp;</th>
+{% endblock table-header %}
+
+
+{% block table-row %}
+<td class="lead">
+  {{ item.name }}
+</td>
+<td class="row-action">
+  <a href="{% url 'misago:admin:permissions:forums:edit' role_id=item.id %}" class="btn btn-primary tooltip-top" title="{% trans "Edit" %}">
+    <span class="fa fa-pencil"></span>
+  </a>
+</td>
+<td class="row-action">
+  <form action="{% url 'misago:admin:permissions:forums:delete' role_id=item.id %}" method="post" class="delete-prompt">
+    <button class="btn btn-danger tooltip-top" title="{% trans "Delete" %}">
+      {% csrf_token %}
+      <span class="fa fa-times"></span>
+    </button>
+  </form>
+</td>
+{% endblock %}
+
+
+{% block emptylist %}
+<td>
+  <p>{% trans "No forum roles are currently defined." %}</p>
+</td>
+{% endblock emptylist %}
+
+
+{% block javascripts %}
+<script type="text/javascript">
+  $(function() {
+    $('.delete-prompt').submit(function() {
+      var decision = confirm("{% trans "Are you sure you want to delete this role?" %}");
+      return decision;
+    });
+  });
+</script>
+{% endblock %}

+ 40 - 0
misago/templates/misago/admin/roles/form.html

@@ -0,0 +1,40 @@
+{% extends "misago/admin/generic/form.html" %}
+{% load crispy_forms_filters i18n %}
+
+
+{% block title %}
+{% if target.pk %}
+{% trans target.name %}
+{% else %}
+{% trans "New role" %}
+{% endif %} | {{ active_link.name }} | {{ block.super }}
+{% endblock title %}
+
+
+{% block page-target %}
+{% if target.pk %}
+{% trans target.name %}
+{% else %}
+{% trans "New role" %}
+{% endif %}
+{% endblock page-target %}
+
+
+{% block form-header %}
+<h1>
+  {% if target.pk %}
+  {% trans target.name %}
+  {% else %}
+  {% trans "New role" %}
+  {% endif %}
+</h1>
+{% endblock %}
+
+
+{% block form-body %}
+<div class="form-body no-fieldsets">
+
+  {{ form.name|as_crispy_field }}
+
+</div>
+{% endblock form-body %}

+ 58 - 0
misago/templates/misago/admin/roles/list.html

@@ -0,0 +1,58 @@
+{% extends "misago/admin/generic/list.html" %}
+{% load i18n %}
+
+
+{% block page-actions %}
+<div class="page-actions">
+  <a href="{% url 'misago:admin:permissions:users:new' %}" class="btn btn-success">
+    <span class="fa fa-plus-circle"></span>
+    {% trans "New role" %}
+  </a>
+</div>
+{% endblock %}
+
+
+{% block table-header %}
+<th>{% trans "Rank" %}</th>
+<th style="width: 1%;">&nbsp;</th>
+<th style="width: 1%;">&nbsp;</th>
+{% endblock table-header %}
+
+
+{% block table-row %}
+<td class="lead">
+  {{ item.name }}
+</td>
+<td class="row-action">
+  <a href="{% url 'misago:admin:permissions:users:edit' role_id=item.id %}" class="btn btn-primary tooltip-top" title="{% trans "Edit" %}">
+    <span class="fa fa-pencil"></span>
+  </a>
+</td>
+<td class="row-action">
+  <form action="{% url 'misago:admin:permissions:users:delete' role_id=item.id %}" method="post" class="delete-prompt">
+    <button class="btn btn-danger tooltip-top" title="{% trans "Delete" %}">
+      {% csrf_token %}
+      <span class="fa fa-times"></span>
+    </button>
+  </form>
+</td>
+{% endblock %}
+
+
+{% block emptylist %}
+<td>
+  <p>{% trans "No user roles are currently defined." %}</p>
+</td>
+{% endblock emptylist %}
+
+
+{% block javascripts %}
+<script type="text/javascript">
+  $(function() {
+    $('.delete-prompt').submit(function() {
+      var decision = confirm("{% trans "Are you sure you want to delete this role?" %}");
+      return decision;
+    });
+  });
+</script>
+{% endblock %}

+ 0 - 1
misago/users/models/rankmodel.py

@@ -1,6 +1,5 @@
 from django.db import models, transaction
 from django.db import models, transaction
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
-from django.utils.translation import ugettext_lazy as _
 from misago.admin import site
 from misago.admin import site
 from misago.core.utils import slugify
 from misago.core.utils import slugify