Browse Source

Getting started with admin generic views.

Rafał Pitoń 11 years ago
parent
commit
09ffa7ef47

+ 1 - 1
misago/admin/hierarchy.py

@@ -122,7 +122,7 @@ class AdminHierarchyBuilder(object):
                         node_added = parent.add_node(node_obj)
                         node_added = parent.add_node(node_obj)
 
 
                     if node_added:
                     if node_added:
-                        namespace = node_obj.namespace
+                        namespace = node.get('namespace') or node_obj.namespace
 
 
                         if namespace not in nodes_dict:
                         if namespace not in nodes_dict:
                             nodes_dict[namespace] = node_obj
                             nodes_dict[namespace] = node_obj

+ 9 - 0
misago/admin/testutils.py

@@ -1,6 +1,15 @@
+from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
+from django.test import TestCase
 
 
 
 
 def admin_login(client, username, password):
 def admin_login(client, username, password):
     client.post(reverse('misago:admin:index'),
     client.post(reverse('misago:admin:index'),
                 data={'username': username, 'password': password})
                 data={'username': username, 'password': password})
+
+
+class AdminTestCase(TestCase):
+    def setUp(self):
+        User = get_user_model()
+        User.objects.create_superuser('TestAdmin', 'admin@test.com', 'Pass.123')
+        admin_login(self.client, 'TestAdmin', 'Pass.123')

+ 0 - 20
misago/admin/urls.py

@@ -30,23 +30,3 @@ def discover_admin_urls():
                 pass
                 pass
 
 
     return admin_patterns
     return admin_patterns
-
-
-"""
-TEST PATTERNS FOR GOD OF TEST PATTERNS
-"""
-
-userpatterns = patterns('misago.admin.views',
-    # top lel at users fake views
-    url(r'^$', 'index.admin_index', name='list'),
-)
-
-
-newpatterns = patterns('',
-    url(r'^', include(userpatterns, namespace='accounts')),
-)
-
-
-urlpatterns += patterns('',
-    url(r'^users/', include(newpatterns, namespace='users')),
-)

+ 6 - 0
misago/admin/views/__init__.py

@@ -39,6 +39,12 @@ def render(request, template, context=None):
 
 
     context.update({'sections': sections, 'actions': actions, 'pages': pages})
     context.update({'sections': sections, 'actions': actions, 'pages': pages})
 
 
+    context['active_link'] = None
+    for item in navigation[-1]:
+        if item['is_active']:
+            context['active_link'] = item
+            break
+
     return dj_render(request, template, context)
     return dj_render(request, template, context)
 
 
 
 

+ 78 - 0
misago/admin/views/generic.py

@@ -0,0 +1,78 @@
+from django.contrib import messages
+from django.shortcuts import redirect
+from django.views.generic import View
+from misago.admin import site
+from misago.admin.views import render
+
+
+class AdminBaseMixin(object):
+    """
+    Admin mixin abstraciton used for configuring admin CRUD views.
+
+    Takes following attributes:
+
+    Model = Model instance
+    message_404 = string used in "requested item not found" messages
+    root_link = name of link leading to root action (eg. list of all items
+    template_dir = directory with templates
+    """
+    Model = None
+    message_404 = None
+    root_link = None
+    template_dir = None
+
+    def get_model(self):
+        return self.Model
+
+
+class AdminView(View):
+    def final_template(self):
+        return '%s/%s' % (self.template_dir, self.template)
+
+    def get_target(self, target):
+        Model = self.get_model()
+        return Model.objects.get(id=target)
+
+    def _get_target(self, request, kwargs):
+        """
+        get_target is called by view to fetch item from DB
+        """
+        Model = self.get_model()
+
+        try:
+            return self.get_target(target)
+        except Model.DoesNotExist:
+            messages.error(request, self.message_404)
+            return redirect(self.root_link)
+
+    def render(self, request, context=None):
+        context = context or {}
+        return render(request, self.final_template(), context)
+
+
+class ItemsList(AdminView):
+    template = 'list.html'
+
+    items_per_page = 0
+
+    def dispatch(self, request, *args, **kwargs):
+        context = {}
+
+        context['items'] = self.get_model().objects.all()
+
+        return self.render(request, context)
+
+
+class FormView(AdminView):
+    template = 'form.html'
+
+    def dispatch(self, request, *args, **kwargs):
+        pass
+
+
+class ButtonView(AdminView):
+    def get(self, request, *args, **kwargs):
+        pass
+
+    def post(self, request, *args, **kwargs):
+        pass

+ 2 - 7
misago/conf/tests/test_admin_views.py

@@ -1,15 +1,10 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
-from django.test import TestCase
-from misago.admin.testutils import admin_login
+from misago.admin.testutils import AdminTestCase
 from misago.conf.models import SettingsGroup
 from misago.conf.models import SettingsGroup
 
 
 
 
-class AdminSettingsViewsTests(TestCase):
-    def setUp(self):
-        User = get_user_model()
-        User.objects.create_superuser('Bob', 'bob@test.com', 'Pass.123')
-        admin_login(self.client, 'Bob', 'Pass.123')
+class AdminSettingsViewsTests(AdminTestCase):
 
 
     def test_link_registered(self):
     def test_link_registered(self):
         """admin index view contains settings link"""
         """admin index view contains settings link"""

+ 19 - 0
misago/templates/misago/admin/generic/base.html

@@ -0,0 +1,19 @@
+{% extends "misago/admin/base.html" %}
+{% load i18n %}
+
+
+{% block content %}
+<div class="page-header">
+  <div class="container">
+    <h1>
+      <span class="{{ active_link.icon }}">
+      {{ active_link.name }}
+    </h1>
+  </div>
+</div>
+
+<div class="container">
+  {% block view %}
+  {% endblock view %}
+</div>
+{% endblock content %}

+ 1 - 0
misago/templates/misago/admin/generic/form.html

@@ -0,0 +1 @@
+{% extends "misago/admin/generic/base.html" %}

+ 79 - 0
misago/templates/misago/admin/generic/list.html

@@ -0,0 +1,79 @@
+{% extends "misago/admin/generic/base.html" %}
+
+
+{% block title %}
+{{ active_link.name }} | {{ block.super }}
+{% endblock title%}
+
+
+{% block view %}
+<div class="table-panel">
+  <table class="table">
+    <tr>
+      {% block table-header %}
+      <th>Lorem</th>
+      <th>Ipsum</th>
+      <th style="width: 136px;">Dolor</th>
+      <th colspan="4">&nbsp;</th>
+      {% endblock table-header %}
+    </tr>
+    {% for item in items %}
+    <tr>
+      {% block table-row %}
+      <td class="lead">Lorem</td>
+      <td>Ipsum</td>
+      <td>
+        <select class="form-control">
+          <option>Pacem</option>
+          <option>Para</option>
+          <option>Bellum</option>
+        </select>
+      </td>
+      <td class="row-action">
+        <button type="button" class="btn btn-success tooltip-top" title="Activate">
+          <span class="fa fa-check"></span>
+        </button>
+      </td>
+      <td class="row-action">
+        <div class="btn-group pull-right">
+          <button type="button" class="btn btn-default dropdown-toggle tooltip-top" data-toggle="dropdown" title="Item options">
+            <span class="fa fa-gear"></span>
+          </button>
+          <ul class="dropdown-menu" role="menu">
+            <li>
+              <a href="#">
+                <span class="fa fa-sort-numeric-desc"></span>
+                Newest
+              </a>
+              <a href="#">
+                <span class="fa fa-sort-numeric-asc"></span>
+                Oldest
+              </a>
+              <a href="#">
+                <span class="fa fa-sort-numeric-desc"></span>
+                Most posts
+              </a>
+              <a href="#">
+                <span class="fa fa-sort-numeric-asc"></span>
+                Least posts
+              </a>
+            </li>
+          </ul>
+        </div>
+      </td>
+      <td class="row-action">
+        <button type="button" class="btn btn-danger tooltip-top" title="Activate">
+          <span class="fa fa-times"></span>
+        </button>
+      </td>
+      <td class="row-select">
+        <label>
+          <input type="checkbox">
+        </label>
+      </td>
+      {% endblock table-row %}
+    </tr>
+    {% endfor %}
+  </table>
+</div><!-- /.table-panel -->
+{% endblock view %}

+ 67 - 0
misago/templates/misago/admin/users/list.html

@@ -0,0 +1,67 @@
+{% extends "misago/admin/generic/list.html" %}
+{% load i18n%}
+
+
+{% block table-header %}
+<th>{% trans "User" %}</th>
+<th>{% trans "E-mail" %}</th>
+{% endblock table-header %}
+
+
+{% block table-row %}
+<td class="lead">
+  {{ item.username }}
+</td>
+<td>
+  <a href="mailto:{{ item.email }}">{{ item.email }}</a>
+</td>
+<td>
+  <select class="form-control">
+    <option>Pacem</option>
+    <option>Para</option>
+    <option>Bellum</option>
+  </select>
+</td>
+<td class="row-action">
+  <button type="button" class="btn btn-success tooltip-top" title="Activate">
+    <span class="fa fa-check"></span>
+  </button>
+</td>
+<td class="row-action">
+  <div class="btn-group pull-right">
+    <button type="button" class="btn btn-default dropdown-toggle tooltip-top" data-toggle="dropdown" title="Item options">
+      <span class="fa fa-gear"></span>
+    </button>
+    <ul class="dropdown-menu" role="menu">
+      <li>
+        <a href="#">
+          <span class="fa fa-sort-numeric-desc"></span>
+          Newest
+        </a>
+        <a href="#">
+          <span class="fa fa-sort-numeric-asc"></span>
+          Oldest
+        </a>
+        <a href="#">
+          <span class="fa fa-sort-numeric-desc"></span>
+          Most posts
+        </a>
+        <a href="#">
+          <span class="fa fa-sort-numeric-asc"></span>
+          Least posts
+        </a>
+      </li>
+    </ul>
+  </div>
+</td>
+<td class="row-action">
+  <button type="button" class="btn btn-danger tooltip-top" title="Activate">
+    <span class="fa fa-times"></span>
+  </button>
+</td>
+<td class="row-select">
+  <label>
+    <input type="checkbox">
+  </label>
+</td>
+{% endblock %}

+ 19 - 0
misago/users/models/usermodel.py

@@ -3,6 +3,7 @@ from django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin,
 from django.db import models
 from django.db import models
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
+from misago.admin import site
 from misago.core.utils import slugify
 from misago.core.utils import slugify
 from misago.users.utils import hash_email
 from misago.users.utils import hash_email
 from misago.users.validators import (validate_email, validate_password,
 from misago.users.validators import (validate_email, validate_password,
@@ -100,3 +101,21 @@ class User(AbstractBaseUser, PermissionsMixin):
     def set_email(self, new_email):
     def set_email(self, new_email):
         self.email = UserManager.normalize_email(new_email)
         self.email = UserManager.normalize_email(new_email)
         self.email_hash = hash_email(new_email)
         self.email_hash = hash_email(new_email)
+
+
+"""register model in misago admin"""
+site.add_node(
+    parent='misago:admin',
+    after='misago:admin:index',
+    namespace='misago:admin:users',
+    link='misago:admin:users:accounts:index',
+    name=_("Users"),
+    icon='fa fa-users')
+
+
+site.add_node(
+    parent='misago:admin:users',
+    namespace='misago:admin:users:accounts',
+    link='misago:admin:users:accounts:index',
+    name=_("User Accounts"),
+    icon='fa fa-users')

+ 20 - 0
misago/users/tests/test_admin_views.py

@@ -0,0 +1,20 @@
+from django.contrib.auth import get_user_model
+from django.core.urlresolvers import reverse
+from misago.admin.testutils import AdminTestCase
+
+
+class UserAdminViewsTests(AdminTestCase):
+    def test_link_registered(self):
+        """admin index view contains users link"""
+        response = self.client.get(reverse('misago:admin:index'))
+
+        self.assertIn(reverse('misago:admin:users:accounts:index'),
+                      response.content)
+
+    def test_list_view(self):
+        """users list view returns 200"""
+        response = self.client.get(
+            reverse('misago:admin:users:accounts:index'))
+
+        self.assertEqual(response.status_code, 200)
+        self.assertIn('TestAdmin', response.content)

+ 0 - 0
misago/users/urls.py → misago/users/urls/__init__.py


+ 17 - 0
misago/users/urls/admin.py

@@ -0,0 +1,17 @@
+from django.conf.urls import patterns, url, include
+from misago.users.views.useradmin import UsersList
+
+
+action_urlpatterns = patterns('',
+    url(r'^$', UsersList.as_view(), name='index'),
+)
+
+
+section_urlpatterns = patterns('',
+    url(r'^accounts/', include(action_urlpatterns, namespace='accounts')),
+)
+
+
+urlpatterns = patterns('',
+    url(r'^users/', include(section_urlpatterns, namespace='users')),
+)

+ 13 - 0
misago/users/views/useradmin.py

@@ -0,0 +1,13 @@
+from django.contrib.auth import get_user_model
+from misago.admin.views import generic
+
+
+class UserAdmin(generic.AdminBaseMixin):
+    template_dir = 'misago/admin/users'
+
+    def get_model(self):
+        return get_user_model()
+
+
+class UsersList(UserAdmin, generic.ItemsList):
+    pass