Browse Source

Categories and redirects views

Rafał Pitoń 10 years ago
parent
commit
77c9cee43d

+ 15 - 1
misago/forums/lists.py

@@ -1,3 +1,5 @@
+from misago.acl import add_acl
+
 from misago.forums.models import Forum
 
 
@@ -19,7 +21,7 @@ def get_forums_list(user, parent=None):
         forums_dict[forum.pk] = forum
         forums_list.append(forum)
 
-        if forum.level > 1:
+        if forum.level > parent_level:
             forums_dict[forum.parent_id].subforums.append(forum)
 
     flat_list = []
@@ -27,4 +29,16 @@ def get_forums_list(user, parent=None):
         has_content = (forum.role != "category" or forum.subforums)
         if forum.level == parent_level and has_content:
             flat_list.append(forum)
+
+    add_acl(user, flat_list)
     return flat_list
+
+
+def get_forum_path(forum):
+    forums_dict = Forum.objects.get_cached_forums_dict()
+
+    forum_path = []
+    while forum.level > 0:
+        forum_path.append(forum)
+        forum = forums_dict[forum.parent_id]
+    return [f for f in reversed(forum_path)]

+ 32 - 0
misago/forums/models.py

@@ -1,5 +1,6 @@
 from urlparse import urlparse
 
+from django.core.urlresolvers import reverse
 from django.db import models
 from django.dispatch import receiver
 from django.utils.translation import ugettext_lazy as _
@@ -10,10 +11,12 @@ from mptt.models import MPTTModel, TreeForeignKey
 from misago.acl import version as acl_version
 from misago.acl.models import BaseRole
 from misago.core import serializer
+from misago.core.cache import cache
 from misago.core.signals import secret_key_changed
 from misago.core.utils import subset_markdown, slugify
 
 
+CACHE_NAME = 'misago_forums_tree'
 FORUMS_TREE_ID = 1
 
 
@@ -30,6 +33,22 @@ class ForumManager(TreeManager):
             qs = self.filter(lft__gt=3)
         return qs.order_by('lft')
 
+    def get_cached_forums_dict(self):
+        forums_dict = cache.get(CACHE_NAME, 'nada')
+        if forums_dict == 'nada':
+            forums_dict = self.get_forums_dict_from_db()
+            cache.set(CACHE_NAME, forums_dict)
+        return forums_dict
+
+    def get_forums_dict_from_db(self):
+        forums_dict = {}
+        for forum in self.all_forums(include_root=True):
+            forums_dict[forum.pk] = forum
+        return forums_dict
+
+    def clear_forums_cache(self):
+        cache.delete(CACHE_NAME)
+
 
 class Forum(MPTTModel):
     parent = TreeForeignKey(
@@ -72,6 +91,7 @@ class Forum(MPTTModel):
         return super(Forum, self).save(*args, **kwargs)
 
     def delete(self, *args, **kwargs):
+        Forum.objects.clear_forums_cache()
         acl_version.invalidate()
         return super(Forum, self).delete(*args, **kwargs)
 
@@ -79,6 +99,18 @@ class Forum(MPTTModel):
     def redirect_host(self):
         return urlparse(self.redirect_url).hostname
 
+    def get_absolute_url(self):
+        if not self.special_role:
+            if self.level == 1:
+                formats = (reverse('misago:index'), self.slug, self.pk)
+                return '%s#%s-%s' % formats
+            else:
+                return reverse('misago:%s' % self.role, kwargs={
+                    'forum_id': self.pk, 'forum_slug': self.slug
+                })
+        else:
+            return None
+
     def set_name(self, name):
         self.name = name
         self.slug = slugify(name)

+ 26 - 1
misago/forums/permissions.py

@@ -1,7 +1,10 @@
+from django.core.exceptions import PermissionDenied
+from django.http import Http404
 from django.utils.translation import ugettext_lazy as _
 
 
 from misago.acl import algebra
+from misago.acl.decorators import require_target_type, return_boolean
 from misago.core import forms
 
 from misago.forums.models import Forum, RoleForumACL, ForumRole
@@ -81,8 +84,30 @@ def build_forum_acl(acl, forum, forums_roles, key_name):
 """
 ACL's for targets
 """
+@require_target_type(Forum)
+def add_acl_to_target(user, target):
+    target.acl['can_see'] = can_see_forum(user, target)
+    target.acl['can_browse'] = can_browse_forum(user, target)
 
 
 """
-ACL's for tests
+ACL tests
 """
+def allow_see_forum(user, target):
+    try:
+        forum_id = target.pk
+    except AttributeError:
+        forum_id = int(target)
+
+    if not forum_id in user.acl['visible_forums']:
+        raise Http404()
+can_see_forum = return_boolean(allow_see_forum)
+
+
+def allow_browse_forum(user, target):
+    target_acl = user.acl['forums'].get(target.id, {'can_browse': False})
+    if not target_acl['can_browse']:
+        message = _('You don\'t have permission '
+                    'to browse "%(forum)s" contents.')
+        raise PermissionDenied(message % {'forum': target.name})
+can_browse_forum = return_boolean(allow_browse_forum)

+ 8 - 0
misago/forums/urls.py

@@ -0,0 +1,8 @@
+from django.conf.urls import patterns, include, url
+
+
+urlpatterns = patterns('misago.forums.views',
+    url(r'^category/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/$', 'category', name='category'),
+    url(r'^redirect/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/$', 'redirect', name='redirect'),
+    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/$', 'forum', name='forum'),
+)

+ 49 - 0
misago/forums/views/__init__.py

@@ -0,0 +1,49 @@
+from django.db.models import F
+from django.shortcuts import redirect as dj_redirect, render
+
+from misago.core.shortcuts import get_object_or_404, validate_slug
+
+from misago.forums.lists import get_forums_list, get_forum_path
+from misago.forums.models import Forum
+from misago.forums.permissions import allow_see_forum, allow_browse_forum
+
+
+def forum_view(role):
+    def wrap(f):
+        def decorator(request, forum_slug, forum_id):
+            allow_see_forum(request.user, forum_id)
+
+            forums = Forum.objects.all_forums()
+            forum = get_object_or_404(forums, pk=forum_id, role=role)
+            validate_slug(forum, forum_slug)
+
+            return f(request, forum)
+        return decorator
+    return wrap
+
+
+@forum_view('category')
+def category(request, forum):
+    if forum.level == 1:
+        return dj_redirect(forum.get_absolute_url())
+    forums = get_forums_list(request.user, forum)
+
+    return render(request, 'misago/forums/category.html', {
+        'category': forum,
+        'forums': forums,
+        'path': get_forum_path(forum),
+    })
+
+
+@forum_view('redirect')
+def redirect(request, forum):
+    if forum.pk not in request.session.get('forum_redirects', []):
+        request.session.setdefault('forum_redirects', []).append(forum.pk)
+        forum.redirects_count = F('redirects_count') + 1
+        forum.save(update_fields=['redirects_count'])
+    return dj_redirect(forum.redirect_url)
+
+
+@forum_view('-')
+def forum(request, forum):
+    pass

+ 5 - 0
misago/forums/views/forumsadmin.py

@@ -61,10 +61,13 @@ class ForumFormMixin(object):
                 form.instance.move_to(form.cleaned_data['new_parent'],
                                       position='last-child')
             form.instance.save()
+            if form.instance.parent_id != form.cleaned_data['new_parent'].pk:
+                Forum.objects.clear_forums_cache()
         else:
             form.instance.insert_at(form.cleaned_data['new_parent'],
                                     position='last-child',
                                     save=True)
+            Forum.objects.clear_forums_cache()
 
         if form.cleaned_data.get('copy_permissions'):
             form.instance.forum_role_set.all().delete()
@@ -129,6 +132,7 @@ class MoveDownForum(ForumAdmin, generic.ButtonView):
 
         if other_target:
             Forum.objects.move_node(target, other_target, 'right')
+            Forum.objects.clear_forums_cache()
 
             message = _('Forum "%s" has been moved below "%s".')
             targets_names = (target.name, other_target.name)
@@ -144,6 +148,7 @@ class MoveUpForum(ForumAdmin, generic.ButtonView):
 
         if other_target:
             Forum.objects.move_node(target, other_target, 'left')
+            Forum.objects.clear_forums_cache()
 
             message = _('Forum "%s" has been moved above "%s".')
             targets_names = (target.name, other_target.name)

+ 11 - 7
misago/static/misago/css/misago/header.less

@@ -66,7 +66,8 @@
 .page-header {
   &>.container {
     .breadcrumb {
-      margin-bottom: (@line-height-computed * @headings-line-height) * -1;
+      margin-top: @line-height-computed * -0.8;
+      margin-bottom: 0px;
 
       color: @breadcrumb-color;
       font-size: @font-size-small;
@@ -78,12 +79,15 @@
         }
 
         a:link, a:visited {
-          color: @breadcrumb-color;
+          color: @state-default;
         }
 
-        a:hover, a:active {
-          color: @breadcrumb-active-color;
-          text-decoration: none;
+        a:hover {
+          color: @state-hover;
+        }
+
+        a:active, a:focus {
+          color: @state-clicked;
         }
 
         span {
@@ -105,8 +109,8 @@
       margin: -3px 0px;
 
       &>form {
-      	margin: 0px;
-      	padding: 0px;
+        margin: 0px;
+        padding: 0px;
       }
 
       .pull-left {

+ 2 - 2
misago/templates/misago/forums/categories.html

@@ -2,11 +2,11 @@
 <div class="forums-list">
 {% for category in categories %}
   {% if category.subforums %}
-  <div class="panel panel-default panel-shadow panel-forums {{ category.css_class }}">
+  <div id="{{ category.slug }}-{{ category.pk }}" class="panel panel-default panel-shadow panel-forums {{ category.css_class }}">
     <div class="panel-heading">
       <h3 class="panel-title">{{ category.name }}</h3>
     </div>
-    {% include "misago/forums/forums.html" %}
+    {% include "misago/forums/forums.html" with forums=category.subforums %}
   </div>
   {% endif %}
 {% endfor %}

+ 48 - 0
misago/templates/misago/forums/category.html

@@ -0,0 +1,48 @@
+{% extends "misago/base.html" %}
+{% load i18n %}
+
+
+{% block title %}{{ category.name }} | {{ block.super }}{% endblock title %}
+
+
+{% block meta-description %}{{ misago_settings.forum_index_meta_description }}{% endblock meta-description %}
+
+
+{% block content %}
+<div class="page-header">
+  <div class="container">
+    {% if path %}
+    <ol class="breadcrumb">
+      {% for crumb in path|slice:":-1" %}
+      <li>
+        <a href="{{ crumb.get_absolute_url }}">{{ crumb.name }}</a>{% if not forloop.last %}<span class="fa fa-chevron-right"></span>{% endif %}
+      </li>
+      {% endfor %}
+    </ol>
+    {% endif %}
+
+    <h1>{{ category.name }}</h1>
+  </div>
+</div>
+<div class="container">
+  {% if category.description %}
+  <div class="lead">
+    {{ category.description|escape|urlize|linebreaks }}
+  </div>
+  {% endif %}
+
+  {% if forums %}
+    {% include "misago/forums/subforums.html" %}
+  {% else %}
+  <p class="lead">
+    {% trans "No forums are set or you don't have permission to see them." %}
+  </p>
+  {% endif %}
+</div>
+{% endblock content %}
+
+
+{% block javascripts %}
+{{ block.super }}
+{% include "misago/forums/js.html" %}
+{% endblock javascripts %}

+ 11 - 7
misago/templates/misago/forums/forums.html

@@ -1,6 +1,6 @@
 {% load humanize i18n misago_capture %}
 <ul class="list-group">
-  {% for forum in category.subforums %}
+  {% for forum in forums %}
   <li class="list-group-item">
     {% if forum.role == 'redirect' %}
     <span class="forum-icon fa fa-link fa-fw"></span>
@@ -8,7 +8,7 @@
     <span class="forum-icon fa fa-comment{% if forum.is_read %}-o{% else %} new{% endif %} fa-fw"></span>
     {% endif %}
 
-    <a href="/" class="item-title forum-name">{{ forum.name }}</a>
+    <a href="{{ forum.get_absolute_url }}" class="item-title forum-name">{{ forum.name }}</a>
 
     <div class="sr-only forum-description">
       {% if forum.description %}
@@ -19,20 +19,24 @@
         <li>
           {% trans "Clicks:" %} <strong>{{ forum.redirects_count|intcomma }}</strong>
         </li>
-        {% else %}
+        {% elif forum.acl.can_browse %}
         <li>
           {% trans "Posts:" %} <strong>{{ forum.posts|intcomma }}</strong>
         </li>
         <li>
           {% trans "Threads:" %} <strong>{{ forum.threads|intcomma }}</strong>
         </li>
+        {% else %}
+        <li>
+          {% trans "You don't have permission to browse this forum." %}
+        </li>
         {% endif %}
       </ul>
     </div>
 
     <footer>
       {% if forum.role == 'redirect' %}
-      <a href="#" class="item-title">{{ forum.redirect_host }}</a>
+      <a href="{{ forum.get_absolute_url }}" class="item-title">{{ forum.redirect_host }}</a>
       <div class="text-muted">
         {% capture as clicks %}<strong>{{ forum.redirects_count|intcomma }}</strong>{% endcapture %}
         {% blocktrans trimmed with clicks=clicks|safe count counter=forum.redirects_count %}
@@ -41,13 +45,13 @@
         {{ clicks }} clicks
         {% endblocktrans %}
       </div>
-      {% elif 0 %}
+      {% elif forum.acl.can_browse %}
       <a href="#" class="item-title">Lorem ipsum dolor met sit amet</a>
       <div class="text-muted">
         <a href="#" class="item-title">somebody</a>, <abbr>32 minutes ago</abbr>
       </div>
       {% else %}
-      <em class="text-muted">Some message</em>
+      <em class="text-muted">{% trans "Can't be browsed" %}</em>
       {% endif %}
     </footer>
 
@@ -59,7 +63,7 @@
       <ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
         {% for subforum in forum.subforums %}
         <li role="presentation">
-          <a role="menuitem" tabindex="-1" href="#">
+          <a role="menuitem" tabindex="-1" href="{{ subforum.get_absolute_url }}">
             {% if subforum.role == 'redirect' %}
             <span class="forum-icon fa fa-link fa-fw"></span>
             {% else %}

+ 9 - 0
misago/templates/misago/forums/subforums.html

@@ -0,0 +1,9 @@
+{% load i18n %}
+<div class="forums-list">
+  <div id="{{ category.slug }}-{{ category.pk }}" class="panel panel-default panel-shadow panel-forums {{ category.css_class }}">
+    <div class="panel-heading">
+      <h3 class="panel-title">{% trans "Subforums" %}</h3>
+    </div>
+    {% include "misago/forums/forums.html" %}
+  </div>
+</div>

+ 1 - 0
misago/urls.py

@@ -15,6 +15,7 @@ urlpatterns += patterns('',
     url(r'^', include('misago.legal.urls')),
     url(r'^', include('misago.users.urls')),
     url(r'^', include('misago.notifications.urls')),
+    url(r'^', include('misago.forums.urls')),
     # UI Server view that handles realtime updates of Misago UI
     url(r'^ui-server/$', 'misago.core.uiviews.uiserver', name="ui_server"),
 )