Browse Source

Merge pull request #63 from rafalp/develop

Merge fixes from dev branch
Rafał Pitoń 12 years ago
parent
commit
6fc5698f67

+ 6 - 7
misago/acl/builder.py

@@ -59,9 +59,8 @@ def acl(request, user):
 
 
 def build_acl(request, roles):
-    acl = ACL(request.monitor['acl_version'])
-    forums = (Forum.objects.filter(special__in=('private_threads', 'reports'))
-              | Forum.objects.get(special='root').get_descendants().order_by('lft'))
+    new_acl = ACL(request.monitor['acl_version'])
+    forums = Forum.objects.get(special='root').get_descendants().order_by('lft')
     perms = []
     forum_roles = {}
 
@@ -74,19 +73,19 @@ def build_acl(request, roles):
     for provider in settings.PERMISSION_PROVIDERS:
         app_module = import_module(provider)
         try:
-            app_module.build(acl, perms)
+            app_module.build(new_acl, perms)
         except AttributeError:
             pass
         try:
-            app_module.build_forums(acl, perms, forums, forum_roles)
+            app_module.build_forums(new_acl, perms, forums, forum_roles)
         except AttributeError:
             pass
 
     for provider in settings.PERMISSION_PROVIDERS:
         app_module = import_module(provider)
         try:
-            app_module.cleanup(acl, perms, forums)
+            app_module.cleanup(new_acl, perms, forums)
         except AttributeError:
             pass
 
-    return acl
+    return new_acl

+ 1 - 1
misago/apps/privatethreads/forms.py

@@ -28,7 +28,7 @@ class InviteUsersMixin(object):
                     if not user.acl(self.request).private_threads.can_participate():
                         raise forms.ValidationError(_('%(user)s cannot participate in private threads.') % {'user': user.username})
                     if (not self.request.acl.private_threads.can_invite_ignoring() and
-                        user.allow_pd_invite(self.request.user)):
+                        not user.allow_pd_invite(self.request.user)):
                         raise forms.ValidationError(_('%(user)s restricts who can invite him to private threads.') % {'user': user.username})
                     self.invite_users.append(user)
                 except User.DoesNotExist:

+ 1 - 1
misago/apps/privatethreads/jumps.py

@@ -62,7 +62,7 @@ class InviteUserView(JumpView, TypeMixin):
             if not acl.private_threads.can_participate():
                     self.request.messages.set_flash(Message(_('%(user)s cannot participate in private threads.') % {'user': user.username}), 'info', 'threads')
             elif (not self.request.acl.private_threads.can_invite_ignoring() and
-                    user.allow_pd_invite(self.request.user)):
+                    not user.allow_pd_invite(self.request.user)):
                 self.request.messages.set_flash(Message(_('%(user)s restricts who can invite him to private threads.') % {'user': user.username}), 'info', 'threads')
             else:
                 self.thread.participants.add(user)

+ 4 - 1
misago/apps/privatethreads/thread.py

@@ -46,10 +46,13 @@ class ThreadView(ThreadBaseView, ThreadModeration, PostsModeration, TypeMixin):
         return actions
 
     def template_vars(self, context):
-        context['participants'] = self.thread.participants.all().prefetch_related('rank')
+        context['participants'] = self.thread.participants.all().order_by('username_slug').prefetch_related('rank')
         context['invite_form'] = FormFields(InviteMemberForm(request=self.request))
         return context
 
+    def tracker_queryset(self):
+        return self.forum.thread_set.filter(participants__id=self.request.user.pk)
+
     def tracker_update(self, last_post):
         super(ThreadView, self).tracker_update(last_post)
         unread = self.tracker.unread_count(self.forum.thread_set.filter(participants__id=self.request.user.pk))

+ 6 - 1
misago/apps/profiles/template.py

@@ -16,7 +16,7 @@ def RequestContext(request, context=None):
     if request.user.is_authenticated() and request.user.pk != context['profile'].pk:
         context['follows'] = request.user.is_following(context['profile'])
         context['ignores'] = request.user.is_ignoring(context['profile'])
-        
+    
     # Find out if this user allows us to see his activity
     if request.user.pk != context['profile'].pk:
         if context['profile'].hide_activity == 2:
@@ -36,6 +36,11 @@ def RequestContext(request, context=None):
         # Fake "right now" time
         context['online'] = {'last': timezone.now()}
 
+    # Sync member
+    if context['profile'].sync_profile():
+        print 'SYNCED!'
+        context['profile'].save(force_update=True)
+
     context['tabs'] = []
     for extension in settings.PROFILE_EXTENSIONS:
         profile_module = import_module(extension + '.profile')

+ 4 - 1
misago/apps/threadtype/thread/views.py

@@ -77,7 +77,10 @@ class ThreadBaseView(ViewBase):
 
     def tracker_update(self, last_post):
         self.tracker.set_read(self.thread, last_post)
-        self.tracker.sync()
+        try:
+            self.tracker.sync(self.tracker_queryset())
+        except AttributeError:
+            self.tracker.sync()
 
     def thread_actions(self):
         pass

+ 1 - 1
misago/apps/usercp/avatar/views.py

@@ -75,7 +75,7 @@ def gallery(request):
             allowed_avatars += gallery['avatars']
 
     if not allowed_avatars:
-        request.messages.set_flash(Message(_("No avatars are avaiable.")), 'info', 'usercp_avatar')
+        request.messages.set_flash(Message(_("No avatar galleries are available at the moment.")), 'info', 'usercp_avatar')
         return redirect(reverse('usercp_avatar'))
 
     message = request.messages.get_message('usercp_avatar')

+ 5 - 1
misago/management/commands/pruneforums.py

@@ -1,7 +1,8 @@
 from datetime import timedelta
 from django.core.management.base import BaseCommand
 from django.utils import timezone
-from misago.models import Forum, Thread
+from misago.models import Forum, Thread, Post
+from misago.monitor import Monitor
 
 class Command(BaseCommand):
     """
@@ -22,4 +23,7 @@ class Command(BaseCommand):
             if deleted:
                 forum.sync()
                 forum.save(force_update=True)
+        monitor = Monitor()
+        monitor['threads'] = Post.objects.count()
+        monitor['posts'] = Post.objects.count()
         self.stdout.write('Forums were pruned.\n')

+ 13 - 4
misago/models/karmamodel.py

@@ -1,6 +1,7 @@
 from django.db import models
+from django.db.models import Sum
 from misago.signals import (merge_post, merge_thread, move_forum_content,
-                            move_post, move_thread, rename_user)
+                            move_post, move_thread, rename_user, sync_user_profile)
 
 class Karma(models.Model):
     forum = models.ForeignKey('Forum')
@@ -53,8 +54,16 @@ move_post.connect(move_posts_handler, dispatch_uid="move_posts_karmas")
 
 def merge_posts_handler(sender, **kwargs):
     Karma.objects.filter(post=sender).update(post=kwargs['new_post'])
-    kwargs['new_post'].upvotes += self.upvotes
-    kwargs['new_post'].downvotes += self.downvotes
-    kwargs['new_post'].score += self.score
+    kwargs['new_post'].upvotes += sender.upvotes
+    kwargs['new_post'].downvotes += sender.downvotes
 
 merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts_karmas")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.karma_given_p = sender.karma_set.filter(score__gt=0).count()
+    sender.karma_given_n = sender.karma_set.filter(score__lt=0).count()
+    sender.karma_p = sender.post_set.all().aggregate(Sum('upvotes'))['upvotes__sum']
+    sender.karma_n = sender.post_set.all().aggregate(Sum('downvotes'))['downvotes__sum']
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_karmas")

+ 8 - 1
misago/models/postmodel.py

@@ -3,7 +3,8 @@ from django.db.models import F
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from misago.signals import (delete_user_content, merge_post, merge_thread,
-                            move_forum_content, move_post, move_thread, rename_user)
+                            move_forum_content, move_post, move_thread,
+                            rename_user, sync_user_profile)
 
 class PostManager(models.Manager):
     def filter_stats(self, start, end):
@@ -154,3 +155,9 @@ def merge_thread_handler(sender, **kwargs):
     Post.objects.filter(thread=sender).update(thread=kwargs['new_thread'], merge=F('merge') + kwargs['merge'])
 
 merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads_posts")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.posts = sender.post_set.count()
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_posts")

+ 16 - 10
misago/models/threadmodel.py

@@ -5,7 +5,7 @@ from django.db.models.signals import pre_delete
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from misago.signals import (delete_user_content, merge_thread, move_forum_content,
-                            move_thread, rename_user)
+                            move_thread, rename_user, sync_user_profile)
 from misago.utils.strings import slugify
 
 class ThreadManager(models.Manager):
@@ -156,17 +156,17 @@ class Thread(models.Model):
             user = watch.user
             if user.pk != request.user.pk:
                 try:
-                    acl = acl(request, user)
-                    acl.forums.allow_forum_view(self.forum)
-                    acl.threads.allow_thread_view(user, self)
-                    acl.threads.allow_post_view(user, self, post)
+                    user_acl = acl(request, user)
+                    user_acl.forums.allow_forum_view(self.forum)
+                    user_acl.threads.allow_thread_view(user, self)
+                    user_acl.threads.allow_post_view(user, self, post)
                     if not user.is_ignoring(request.user):
                         user.email_user(
-                            request,
-                            '%s_reply_notification' % thread_type,
-                            _('New reply in thread "%(thread)s"') % {'thread': self.name},
-                            {'author': request.user, 'post': post, 'thread': self}
-                            )
+                                        request,
+                                        '%s_reply_notification' % thread_type,
+                                        _('New reply in thread "%(thread)s"') % {'thread': self.name},
+                                        {'author': request.user, 'post': post, 'thread': self}
+                                        )
                 except (ACLError403, ACLError404):
                     pass
 
@@ -206,3 +206,9 @@ def delete_user_handler(sender, instance, using, **kwargs):
                 thread.delete()
 
 pre_delete.connect(delete_user_handler, dispatch_uid="delete_user_participations")
+
+
+def sync_user_handler(sender, **kwargs):
+    sender.threads = sender.thread_set.count()
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_threads")

+ 18 - 3
misago/models/usermodel.py

@@ -1,4 +1,5 @@
 import hashlib
+from datetime import timedelta
 import math
 from random import choice
 from path import path
@@ -14,7 +15,7 @@ from django.utils import timezone as tz_util
 from django.utils.translation import ugettext_lazy as _
 from misago.acl.builder import build_acl
 from misago.monitor import Monitor
-from misago.signals import delete_user_content, rename_user
+from misago.signals import delete_user_content, rename_user, sync_user_profile
 from misago.utils.avatars import avatar_size
 from misago.utils.strings import random_string, slugify
 from misago.validators import validate_username, validate_password, validate_email
@@ -508,8 +509,13 @@ class User(models.Model):
     def get_date(self):
         return self.join_date
 
-    def sync_user(self):
-        pass
+    def sync_profile(self):
+        if (settings.PROFILES_SYNC_FREQUENCY > 0 and
+                self.last_sync <= tz_util.now() - timedelta(days=settings.PROFILES_SYNC_FREQUENCY)):
+            sync_user_profile.send(sender=self)
+            self.last_sync = tz_util.now()
+            return True
+        return False
 
 
 class Guest(object):
@@ -555,3 +561,12 @@ class Crawler(Guest):
     def is_crawler(self):
         return True
 
+
+"""
+Signals handlers
+"""
+def sync_user_handler(sender, **kwargs):
+    sender.following = sender.follows.count()
+    sender.followers = sender.follows_set.count()
+
+sync_user_profile.connect(sync_user_handler, dispatch_uid="sync_user_follows")

+ 9 - 4
misago/readstrackers.py

@@ -67,20 +67,22 @@ class ThreadsTracker(object):
             except KeyError:
                 self.need_create = thread
 
-    def unread_count(self, queryset=None):
+    def unread_count(self, queryset):
         try:
             return self.unread_threads
         except AttributeError:
             self.unread_threads = 0
             if not queryset:
-                queryset = self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set)
+                queryset = self.default_queryset()
             for thread in queryset.filter(last__gte=self.record.cleared):
                 if not self.is_read(thread):
                     self.unread_threads += 1
             return self.unread_threads
 
-    def sync(self):
+    def sync(self, queryset=None):
         now = timezone.now()
+        if not queryset:
+            queryset = self.default_queryset()
 
         if self.need_create:
             new_record = ThreadRead(
@@ -97,10 +99,13 @@ class ThreadsTracker(object):
             self.need_update.save(force_update=True)
 
         if self.need_create or self.need_update:
-            if not self.unread_count():
+            if not self.unread_count(queryset):
                 self.record.cleared = now
             self.record.updated = now
             if self.record.pk:
                 self.record.save(force_update=True)
             else:
                 self.record.save(force_insert=True)
+
+    def default_queryset(self):
+        return self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set)

+ 5 - 1
misago/settings_base.py

@@ -42,12 +42,16 @@ LOCALE_PATHS = (
 # If DEBUG_MODE is on, all emails will be sent to this address instead of real recipient.
 CATCH_ALL_EMAIL_ADDRESS = ''
 
-# Forums and threads read tracker length (days
+# Forums and threads read tracker length (days)
 # Enter 0 to turn tracking off
 # The bigger the number, then longer tracker keeps threads reads
 # information and the more costful it is to track reads
 READS_TRACKER_LENGTH = 7
 
+# Min. number of days between synchronisating member profiles
+# Allows you to keep your member profiles up to date, enter 0 to never sync
+PROFILES_SYNC_FREQUENCY = 7
+
 # Heartbeat Path for crons
 # Use this path if you wish to keep Misago alive using separate cron
 # By quering this path from your cron you'll keep Misago's base clean

+ 2 - 1
misago/signals.py

@@ -7,4 +7,5 @@ merge_thread = django.dispatch.Signal(providing_args=["new_thread", "merge"])
 move_forum_content = django.dispatch.Signal(providing_args=["move_to"])
 move_post = django.dispatch.Signal(providing_args=["move_to"])
 move_thread = django.dispatch.Signal(providing_args=["move_to"])
-rename_user = django.dispatch.Signal()
+rename_user = django.dispatch.Signal()
+sync_user_profile = django.dispatch.Signal()

+ 1 - 1
templates/cranefly/private_threads/posting.html

@@ -117,7 +117,7 @@
 {% url 'private_thread_reply' thread=thread.pk, slug=thread.slug %}
 {%- endif -%}
 {%- elif action == 'edit_reply' -%}
-{% url 'post_edit' thread=thread.pk, slug=thread.slug, post=post.pk %}
+{% url 'private_post_edit' thread=thread.pk, slug=thread.slug, post=post.pk %}
 {%- endif %}
 {%- endmacro %}
 

+ 1 - 1
templates/cranefly/private_threads/thread.html

@@ -390,7 +390,7 @@
             <img src="{{ participant.get_avatar(24) }}" alt="" class="avatar-small">
             <a href="{% url 'user' username=participant.username_slug, user=participant.pk %}">{{ participant.username }}</a>
             {% if user.pk == thread.start_poster_id or acl.private_threads.is_mod() %}
-            <form class="form-inline {% if user.pk == thread.start_poster_id %}leave-form{% else %}kick-form{% endif %} tooltip-left" action="{% url 'private_thread_remove_user' thread=thread.pk, slug=thread.slug %}" method="post" title="{% if participant.pk == user.pk %}{% trans %}Leave this thread{% endtrans %}{% else %}{% trans %}Remove from this thread{% endtrans %}{% endif %}">
+            <form class="form-inline {% if user.pk == participant.pk %}leave-form{% else %}kick-form{% endif %} tooltip-left" action="{% url 'private_thread_remove_user' thread=thread.pk, slug=thread.slug %}" method="post" title="{% if participant.pk == user.pk %}{% trans %}Leave this thread{% endtrans %}{% else %}{% trans %}Remove from this thread{% endtrans %}{% endif %}">
               <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
               <input type="hidden" name="retreat" value="{{ request_path }}">
               <input type="hidden" name="user" value="{{ participant.pk }}">

+ 22 - 6
templates/cranefly/watched.html

@@ -37,12 +37,12 @@
         {% for thread in threads %}
         <tr id="watch-{{ loop.index }}">
           <td>
-            <a href="{% url 'thread_new' thread=thread.pk, slug=thread.slug %}" class="thread-icon{% if not thread.is_read %} thread-new{% endif %} tooltip-top" title="{% if not thread.is_read -%}
+            <a href="{{ thread_url(thread, 'new') }}" class="thread-icon{% if not thread.is_read %} thread-new{% endif %} tooltip-top" title="{% if not thread.is_read -%}
             {% trans %}Click to see first unread post{% endtrans %}
             {%- else -%}
             {% trans %}Click to see last post{% endtrans %}
             {%- endif %}"><i class="icon-comment"></i></a>
-            <a href="{% url 'thread' thread=thread.pk, slug=thread.slug %}" class="thread-name">{{ thread.name }}</a>
+            <a href="{{ thread_url(thread) }}" class="thread-name">{{ thread.name }}</a>
             <span class="thread-details">
               {% trans user=thread_starter(thread), forum=thread_forum(thread), start=thread.start|reltimesince|low %}by {{ user }} in {{ forum }} {{ start }}{% endtrans %}
             </span>
@@ -53,13 +53,13 @@
             </div>
           </td>
           <td class="watched-thread-flags">
-            <form action="{% url 'thread_unwatch' thread=thread.pk, slug=thread.slug %}" method="post">
+            <form action="{{ thread_url(thread, 'unwatch') }}" method="post">
               <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
               <input type="hidden" name="retreat" value="{{ delete_retreat(loop) }}">
               <button type="submit" class="btn btn-danger tooltip-top" title="{% trans %}Unwatch{% endtrans %}"><i class="icon-remove"></i></button>
             </form>
 
-            <form action="{% if thread.send_email %}{% url 'thread_unwatch_email' thread=thread.pk, slug=thread.slug %}{% else %}{% url 'thread_watch_email' thread=thread.pk, slug=thread.slug %}{% endif %}" method="post">
+            <form action="{% if thread.send_email %}{{ thread_url(thread, 'unwatch_email') }}{% else %}{{ thread_url(thread, 'watch_email') }}{% endif %}" method="post">
               <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
               <input type="hidden" name="retreat" value="{{ request_path }}#watch-{{ loop.index }}">
               <button type="submit" class="btn btn-{% if thread.send_email %}success{% else %}inverse{% endif %} tooltip-top" title="{% if thread.send_email %}{% trans %}Don't notify with e-mail{% endtrans %}{% else %}{% trans %}Notify with e-mail{% endtrans %}{% endif %}"><i class="icon-envelope"></i></button>
@@ -82,6 +82,16 @@
 {% endblock %}
 
 
+{% macro thread_url(thread, route='') -%}{% filter url(thread=thread.pk, slug=thread.slug) %}{% filter trim %}
+{% if thread.forum_id == private_threads.pk -%}
+private_thread
+{%- elif thread.forum_id == reports.pk -%}
+report
+{%- else -%}
+thread
+{%- endif -%}{%- if route %}_{{ route }}{% endif %}{% endfilter %}{% endfilter %}
+{%- endmacro%}
+
 {% macro replies(thread_replies) -%}
 {% trans count=thread_replies, replies=('<strong>' ~ (thread_replies|intcomma) ~ '</strong>')|safe -%}
 {{ replies }} reply
@@ -94,9 +104,15 @@
 {% if thread.start_poster_id %}<a href="{% url 'user' user=thread.start_poster_id, username=thread.start_poster_slug %}" class="user-link">{{ thread.start_poster_name }}</a>{% else %}{{ thread.start_poster_name }}{% endif %}
 {%- endmacro %}
 
-{% macro thread_forum(thread) -%}
+{% macro thread_forum(thread) -%}{% filter trim %}
+{% if thread.forum_id == private_threads.pk %}
+<a href="{% url 'private_threads' %}" class="forum-link">{% trans %}Private Threads{% endtrans %}</a>
+{% elif thread.forum_id == reports.pk %}
+TODO!
+{% else %}
 <a href="{% url 'forum' forum=thread.forum_id, slug=thread.forum.slug %}" class="forum-link">{{ thread.forum.name }}</a>
-{%- endmacro %}
+{% endif%}
+{% endfilter %}{%- endmacro %}
 
 {% macro thread_reply(thread) -%}
 {% if thread.last_poster_id %}<a href="{% url 'user' user=thread.last_poster_id, username=thread.last_poster_slug %}" class="user-link">{{ thread.last_poster_name }}</a>{% else %}{{ thread.last_poster_name }}{% endif %}