Browse Source

Fancy tracking of unread private threads.

Ralfp 12 years ago
parent
commit
c786063174

+ 4 - 0
misago/apps/privatethreads/jumps.py

@@ -66,6 +66,8 @@ class InviteUserView(JumpView, TypeMixin):
                 self.request.messages.set_flash(Message(_('%(user)s does not wish to participate in your private threads.') % {'user': user.username}), 'info', 'threads')
                 self.request.messages.set_flash(Message(_('%(user)s does not wish to participate in your private threads.') % {'user': user.username}), 'info', 'threads')
             else:
             else:
                 self.thread.participants.add(user)
                 self.thread.participants.add(user)
+                user.sync_pds = True
+                user.save(force_update=True)
                 user.email_user(self.request, 'private_thread_invite', _("You've been invited to private thread \"%(thread)s\" by %(user)s") % {'thread': self.thread.name, 'user': self.request.user.username}, {'author': self.request.user, 'thread': self.thread})
                 user.email_user(self.request, 'private_thread_invite', _("You've been invited to private thread \"%(thread)s\" by %(user)s") % {'thread': self.thread.name, 'user': self.request.user.username}, {'author': self.request.user, 'thread': self.thread})
                 self.thread.last_post.set_checkpoint(self.request, 'invited', user)
                 self.thread.last_post.set_checkpoint(self.request, 'invited', user)
                 self.thread.last_post.save(force_update=True)
                 self.thread.last_post.save(force_update=True)
@@ -88,6 +90,8 @@ class RemoveUserView(JumpView, TypeMixin):
             self.thread.participants.remove(user)
             self.thread.participants.remove(user)
             self.thread.threadread_set.filter(id=user.pk).delete()
             self.thread.threadread_set.filter(id=user.pk).delete()
             self.thread.watchedthread_set.filter(id=user.pk).delete()
             self.thread.watchedthread_set.filter(id=user.pk).delete()
+            user.sync_pds = True
+            user.save(force_update=True)
             # If there are no more participants in thread, remove it
             # If there are no more participants in thread, remove it
             if self.thread.participants.count() == 0:
             if self.thread.participants.count() == 0:
                 self.thread.delete()
                 self.thread.delete()

+ 2 - 0
misago/apps/privatethreads/mixins.py

@@ -25,6 +25,8 @@ class TypeMixin(object):
         if sync_last_post:
         if sync_last_post:
             self.thread.last_post.save(force_update=True)
             self.thread.last_post.save(force_update=True)
 
 
+    def force_stats_sync(self):
+        self.thread.participants.exclude(id=self.request.user.id).update(sync_pds=True)
                 
                 
     def whitelist_mentions(self):
     def whitelist_mentions(self):
         participants = self.thread.participants.all()
         participants = self.thread.participants.all()

+ 6 - 2
misago/apps/privatethreads/posting.py

@@ -17,8 +17,8 @@ class NewThreadView(NewThreadBaseView, TypeMixin):
     def after_form(self, form):
     def after_form(self, form):
         self.thread.participants.add(self.request.user)
         self.thread.participants.add(self.request.user)
         self.invite_users(form.invite_users)
         self.invite_users(form.invite_users)
-
         self.whitelist_mentions()
         self.whitelist_mentions()
+        self.force_stats_sync()
 
 
     def response(self):
     def response(self):
         if self.post.moderated:
         if self.post.moderated:
@@ -43,8 +43,12 @@ class NewReplyView(NewReplyBaseView, TypeMixin):
     form_type = NewReplyForm
     form_type = NewReplyForm
 
 
     def after_form(self, form):
     def after_form(self, form):
-        self.invite_users(form.invite_users)
+        try:
+            self.invite_users(form.invite_users)
+        except AttributeError:
+            pass
         self.whitelist_mentions()
         self.whitelist_mentions()
+        self.force_stats_sync()
 
 
     def response(self):
     def response(self):
         if self.post.moderated:
         if self.post.moderated:

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

@@ -48,4 +48,10 @@ class ThreadView(ThreadBaseView, ThreadModeration, PostsModeration, TypeMixin):
     def template_vars(self, context):
     def template_vars(self, context):
         context['participants'] = self.thread.participants.all().prefetch_related('rank')
         context['participants'] = self.thread.participants.all().prefetch_related('rank')
         context['invite_form'] = FormFields(InviteMemberForm(request=self.request))
         context['invite_form'] = FormFields(InviteMemberForm(request=self.request))
-        return context
+        return context
+
+    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))
+        self.request.user.sync_unread_pds(unread)
+        self.request.user.save(force_update=True)

+ 5 - 2
misago/apps/threadtype/thread/views.py

@@ -65,8 +65,7 @@ class ThreadBaseView(ViewBase):
         last_post = self.posts[len(self.posts) - 1]
         last_post = self.posts[len(self.posts) - 1]
 
 
         if not self.tracker.is_read(self.thread):
         if not self.tracker.is_read(self.thread):
-            self.tracker.set_read(self.thread, last_post)
-            self.tracker.sync()
+            self.tracker_update(last_post)
 
 
         if self.watcher and last_post.date > self.watcher.last_read:
         if self.watcher and last_post.date > self.watcher.last_read:
             self.watcher.last_read = timezone.now()
             self.watcher.last_read = timezone.now()
@@ -76,6 +75,10 @@ class ThreadBaseView(ViewBase):
             for karma in Karma.objects.filter(post_id__in=posts_dict.keys()).filter(user=self.request.user):
             for karma in Karma.objects.filter(post_id__in=posts_dict.keys()).filter(user=self.request.user):
                 posts_dict[karma.post_id].karma_vote = karma
                 posts_dict[karma.post_id].karma_vote = karma
 
 
+    def tracker_update(self, last_post):
+        self.tracker.set_read(self.thread, last_post)
+        self.tracker.sync()
+
     def thread_actions(self):
     def thread_actions(self):
         pass
         pass
 
 

+ 9 - 0
misago/management/commands/forcepdssync.py

@@ -0,0 +1,9 @@
+from django.core.management.base import BaseCommand
+from misago.models import User
+
+class Command(BaseCommand):
+    help = 'Updates unread Private Threads counters update for all users'
+
+    def handle(self, *args, **options):
+        User.objects.update(sync_pds=True)
+        self.stdout.write('\nUsers accounts were set to sync unread private threads stat on next visit.\n')

+ 13 - 0
misago/middleware/privatethreads.py

@@ -0,0 +1,13 @@
+from misago.models import Forum, Thread
+from misago.readstrackers import ThreadsTracker
+
+class PrivateThreadsMiddleware(object):
+    def process_request(self, request):
+        if (request.user.is_authenticated() and
+                request.acl.private_threads.can_participate() and
+                request.user.sync_pds):
+            forum = Forum.objects.special_model('private_threads')
+            tracker = ThreadsTracker(request, forum)
+            unread_pds = tracker.unread_count(forum.thread_set.filter(participants__id=request.user.pk))
+            request.user.sync_unread_pds(unread_pds)
+            request.user.save(force_update=True)

+ 5 - 0
misago/models/usermodel.py

@@ -164,6 +164,7 @@ class User(models.Model):
     alerts = models.PositiveIntegerField(default=0)
     alerts = models.PositiveIntegerField(default=0)
     alerts_date = models.DateTimeField(null=True, blank=True)
     alerts_date = models.DateTimeField(null=True, blank=True)
     unread_pds = models.PositiveIntegerField(default=0)
     unread_pds = models.PositiveIntegerField(default=0)
+    sync_pds = models.BooleanField(default=False)
     activation = models.IntegerField(default=0)
     activation = models.IntegerField(default=0)
     token = models.CharField(max_length=12, null=True, blank=True)
     token = models.CharField(max_length=12, null=True, blank=True)
     avatar_ban = models.BooleanField(default=False)
     avatar_ban = models.BooleanField(default=False)
@@ -489,6 +490,10 @@ class User(models.Model):
         self.alerts += 1
         self.alerts += 1
         return Alert(user=self, message=message, date=tz_util.now())
         return Alert(user=self, message=message, date=tz_util.now())
 
 
+    def sync_unread_pds(self, unread):
+        self.unread_pds = unread
+        self.sync_pds = False
+
     def get_date(self):
     def get_date(self):
         return self.join_date
         return self.join_date
 
 

+ 13 - 5
misago/readstrackers.py

@@ -67,6 +67,18 @@ class ThreadsTracker(object):
             except KeyError:
             except KeyError:
                 self.need_create = thread
                 self.need_create = thread
 
 
+    def unread_count(self, queryset=None):
+        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)
+            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):
         now = timezone.now()
         now = timezone.now()
 
 
@@ -85,11 +97,7 @@ class ThreadsTracker(object):
             self.need_update.save(force_update=True)
             self.need_update.save(force_update=True)
 
 
         if self.need_create or self.need_update:
         if self.need_create or self.need_update:
-            unread_threads = 0
-            for thread in self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set.filter(last__gte=self.record.cleared)):
-                if not self.is_read(thread):
-                    unread_threads += 1
-            if not unread_threads:
+            if not self.unread_count():
                 self.record.cleared = now
                 self.record.cleared = now
             self.record.updated = now
             self.record.updated = now
             if self.record.pk:
             if self.record.pk:

+ 1 - 0
misago/settings_base.py

@@ -102,6 +102,7 @@ MIDDLEWARE_CLASSES = (
     'misago.middleware.messages.MessagesMiddleware',
     'misago.middleware.messages.MessagesMiddleware',
     'misago.middleware.user.UserMiddleware',
     'misago.middleware.user.UserMiddleware',
     'misago.middleware.acl.ACLMiddleware',
     'misago.middleware.acl.ACLMiddleware',
+    'misago.middleware.privatethreads.PrivateThreadsMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.common.CommonMiddleware',
 )
 )
 
 

+ 1 - 1
templates/cranefly/layout.html

@@ -50,7 +50,7 @@
           <li class="user-profile"><a href="{% url 'user' user=user.id, username=user.username_slug %}" title="{% trans %}Go to your profile{% endtrans %}" class="tooltip-bottom"><div><img src="{{ user.get_avatar(28) }}" alt=""> {{ user.username }}</div></a></li>
           <li class="user-profile"><a href="{% url 'user' user=user.id, username=user.username_slug %}" title="{% trans %}Go to your profile{% endtrans %}" class="tooltip-bottom"><div><img src="{{ user.get_avatar(28) }}" alt=""> {{ user.username }}</div></a></li>
           <li><a href="{% url 'alerts' %}" title="{% if user.alerts %}{% trans %}You have new notifications!{% endtrans %}{% else %}{% trans %}Your Notifications{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-asterisk"></i>{% if user.alerts %}<span class="label label-important">{{ user.alerts }}</span>{% endif %}</a></li>
           <li><a href="{% url 'alerts' %}" title="{% if user.alerts %}{% trans %}You have new notifications!{% endtrans %}{% else %}{% trans %}Your Notifications{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-asterisk"></i>{% if user.alerts %}<span class="label label-important">{{ user.alerts }}</span>{% endif %}</a></li>
           {% if settings.enable_private_threads and acl.forums.can_browse(private_threads) and acl.threads.can_read_threads(private_threads) %}
           {% if settings.enable_private_threads and acl.forums.can_browse(private_threads) and acl.threads.can_read_threads(private_threads) %}
-          <li><a href="{% url 'private_threads' %}" title="{% if user.unread_pds %}{% trans %}You have new replies in your Private Threads!{% endtrans %}{% else %}{% trans %}Your Private Threads{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-inbox"></i>{% if user.unread_pds %}<span class="label label-important">{{ user.unread_pds }}</span>{% endif %}</a></li>
+          <li><a href="{% url 'private_threads' %}" title="{% if user.unread_pds %}{% trans %}There are unread Private Threads!{% endtrans %}{% else %}{% trans %}Your Private Threads{% endtrans %}{% endif %}" class="tooltip-bottom"><i class="icon-inbox"></i>{% if user.unread_pds %}<span class="label label-important">{{ user.unread_pds }}</span>{% endif %}</a></li>
           {% endif %}
           {% endif %}
           <li><a href="{% url 'newsfeed' %}" title="{% trans %}Your News Feed{% endtrans %}" class="tooltip-bottom"><i class="icon-signal"></i></a></li>
           <li><a href="{% url 'newsfeed' %}" title="{% trans %}Your News Feed{% endtrans %}" class="tooltip-bottom"><i class="icon-signal"></i></a></li>
           <li><a href="{% url 'watched_threads' %}" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>
           <li><a href="{% url 'watched_threads' %}" title="{% trans %}Threads you are watching{% endtrans %}" class="tooltip-bottom"><i class="icon-bookmark"></i></a></li>