Browse Source

Ranks online on forums index

Rafał Pitoń 10 years ago
parent
commit
1271d17196

+ 5 - 1
misago/core/views.py

@@ -1,5 +1,9 @@
 from django.shortcuts import render
 
+from misago.users.online.ranks import get_ranks_online
+
 
 def forum_index(request):
-    return render(request, 'misago/index.html')
+    return render(request, 'misago/index.html', {
+        'ranks_online': get_ranks_online()
+    })

+ 59 - 0
misago/static/misago/css/misago/index.less

@@ -0,0 +1,59 @@
+//
+// Forum Index
+// --------------------------------------------------
+
+
+// Ranks online list
+//
+//==
+.ranks-online {
+  .rank-online {
+    margin-bottom: @line-height-computed;
+
+    h3 {
+      margin-top: 0px;
+
+      color: @text-muted;
+      font-size: @font-size-large;
+    }
+
+    ul {
+      background-color: @panel-bg;
+      border: 1px solid @panel-default-border;
+      border-radius: @border-radius-base;
+
+      li {
+        a {
+          border-bottom: solid 1px @panel-inner-border;
+          display: block;
+          padding: @padding-small-vertical @padding-small-horizontal;
+
+          font-size: @font-size-large;
+
+          img {
+            border-radius: @border-radius-base;
+            height: 26px;
+            margin-right: @padding-small-vertical * 0.7;
+          }
+
+          span {
+            position: relative;
+            top: 1px;
+          }
+
+          small {
+            float: right;
+            position: relative;
+            top: 3px;
+
+            color: @text-muted;
+          }
+        }
+      }
+
+      &:last-child {
+        border-bottom: none;
+      }
+    }
+  }
+}

+ 1 - 0
misago/static/misago/css/misago/misago.less

@@ -25,6 +25,7 @@
 
 // Pages
 @import "errorpages.less";
+@import "index.less";
 @import "profile.less";
 @import "signin.less";
 @import "warnings.less";

+ 49 - 0
misago/static/misago/css/misago/typography.less

@@ -3,12 +3,61 @@
 // --------------------------------------------------
 
 
+.item-title {
+  color: @state-hover;
+
+  &:link, &:hover, &:visited {
+    color: @state-hover;
+  }
+
+  &:active {
+    color: @state-clicked;
+  }
+
+  &.combined {
+    &:hover {
+      text-decoration: none;
+
+      &>span {
+        text-decoration: underline;
+      }
+    }
+
+    &:active {
+      text-decoration: none;
+
+      &>span {
+        text-decoration: underline;
+      }
+    }
+  }
+}
+
+
 .text-muted {
   strong {
     color: darken(@text-muted, 20%);
+    font-weight: normal;
   }
 
   a:link, a:visited, a:active, a:hover {
     color: @link-muted-color;
   }
+
+  .item-title {
+    color: darken(@text-muted, 20%);
+    font-weight: bold;
+
+    &:link, &:visited {
+      color: darken(@text-muted, 20%);
+    }
+
+    &:hover {
+      color: @state-hover;
+    }
+
+    &:active {
+      color: @state-clicked;
+    }
+  }
 }

+ 2 - 2
misago/static/misago/css/misago/variables.less

@@ -275,12 +275,12 @@
 @panel-bg:                    #fff;
 @panel-body-padding:          15px;
 
+@panel-default-border:        darken(@body-bg, 15%);
+
 //** Border color for elements within panels
 @panel-inner-border:          darken(@body-bg, 8%);
 @panel-footer-bg:             @panel-bg;
 
-@panel-default-border:        darken(@body-bg, 15%);
-
 //** User cards
 @user-card-bg:                         @body-bg;
 @user-card-border:                     darken(@body-bg, 15%);

+ 9 - 0
misago/static/misago/css/ranks.less

@@ -13,3 +13,12 @@
     }
   }
 }
+
+
+.ranks-online {
+  .rank-online.rank-team {
+    small {
+      color: @brand-accent;
+    }
+  }
+}

+ 14 - 1
misago/templates/misago/index.html

@@ -17,6 +17,19 @@
 {% include "misago/jumbotron_indexonly.html" %}
 
 <div class="container">
-  Hello world, I'm placeholder index template!
+  <div class="row">
+    <div class="col-md-8">
+
+      Hello world, I'm placeholder index template!
+
+    </div>
+    <div class="col-md-4">
+
+      {% if ranks_online %}
+      {% include "misago/ranks_online.html" %}
+      {% endif %}
+
+    </div>
+  </div>
 </div>
 {% endblock content %}

+ 25 - 0
misago/templates/misago/ranks_online.html

@@ -0,0 +1,25 @@
+{% load i18n misago_avatars %}
+<div class="ranks-online">
+  {% for rank in ranks_online %}
+  <div class="rank-online {% if rank.css_class %}rank-{{ rank.css_class }}{% endif %}">
+    <h3>
+      {% blocktrans trimmed with rank=rank.name %}
+      {{ rank }} online
+      {% endblocktrans %}
+    </h3>
+    <ul class="list-unstyled">
+      {% for user in rank.online %}
+      <li>
+        <a href="{% url USER_PROFILE_URL user_slug=user.slug user_id=user.id %}" class="item-title combined">
+          <img src="{{ user.id|avatar:32 }}" alt="{% trans "Avatar" %}">
+          <span>{{ user.username }}</span>
+          {% if user.title %}
+          <small>{{ user.title }}</small>
+          {% endif %}
+        </a>
+      </li>
+      {% endfor %}
+    </ul>
+    {% endfor %}
+  </div>
+</div>

+ 1 - 1
misago/users/authbackends.py

@@ -22,7 +22,7 @@ class MisagoBackend(ModelBackend):
         UserModel = get_user_model()
         try:
             manager = UserModel._default_manager
-            relations = ('online_tracker', 'ban_cache')
+            relations = ('rank', 'online_tracker', 'ban_cache')
             return manager.select_related(*relations).get(pk=user_id)
         except UserModel.DoesNotExist:
             return None

+ 6 - 16
misago/users/middleware.py

@@ -6,6 +6,7 @@ from django.utils import timezone
 
 from misago.users.bans import get_request_ip_ban, get_user_ban
 from misago.users.models import AnonymousUser, Online
+from misago.users.online import tracker
 from misago.users.views import avatarserver
 
 
@@ -42,29 +43,18 @@ class OnlineTrackerMiddleware(object):
             try:
                 request._misago_online_tracker = request.user.online_tracker
             except Online.DoesNotExist:
-                online_tracker = Online.objects.create(
-                    user=request.user, current_ip=request._misago_real_ip)
-                request.user.online_tracker = online_tracker
-                request._misago_online_tracker = online_tracker
+                tracker.start_tracking(request, request.user)
         else:
             request._misago_online_tracker = None
 
     def process_response(self, request, response):
         if hasattr(request, '_misago_online_tracker'):
-            tracker = request._misago_online_tracker
+            online_tracker = request._misago_online_tracker
 
-            if tracker:
+            if online_tracker:
                 if request.user.is_anonymous():
-                    # User logged off, update his last visit and blam tracker
-                    user = tracker.user
-                    user.last_login = tracker.last_click
-                    user.last_ip = tracker.current_ip
-                    user.save(update_fields=['last_login', 'last_ip'])
-                    tracker.delete()
+                    tracker.stop_tracking(request, online_tracker)
                 else:
-                    # Bump user's tracker time
-                    tracker.current_ip = request._misago_real_ip
-                    tracker.last_click = timezone.now()
-                    tracker.save(update_fields=['last_click', 'current_ip'])
+                    tracker.update_tracker(request, online_tracker)
 
         return response

+ 1 - 0
misago/users/migrations/0001_initial.py

@@ -85,6 +85,7 @@ class Migration(migrations.Migration):
             fields=[
                 ('current_ip', models.GenericIPAddressField()),
                 ('last_click', models.DateTimeField(default=django.utils.timezone.now)),
+                ('is_visible_on_index', models.BooleanField(default=False)),
                 ('user', models.OneToOneField(primary_key=True, serialize=False, to=settings.AUTH_USER_MODEL)),
             ],
             options={

+ 2 - 2
misago/users/migrations/0004_default_ranks.py

@@ -11,8 +11,8 @@ def create_default_ranks(apps, schema_editor):
     Rank = apps.get_model('misago_users', 'Rank')
 
     team = Rank.objects.create(
-        name=_("Forum Team"),
-        slug=slugify(_("Forum Team")),
+        name=_("Forum team"),
+        slug=slugify(_("Forum team")),
         title=_("Team"),
         css_class='team',
         is_tab=True,

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

@@ -377,6 +377,7 @@ class Online(models.Model):
                                 related_name='online_tracker')
     current_ip = models.GenericIPAddressField()
     last_click = models.DateTimeField(default=timezone.now)
+    is_visible_on_index = models.BooleanField(default=False)
 
 
 class UsernameChange(models.Model):

+ 0 - 0
misago/users/online/__init__.py


+ 61 - 0
misago/users/online/ranks.py

@@ -0,0 +1,61 @@
+from datetime import timedelta
+
+from django.utils import timezone
+
+from misago.core.cache import cache
+
+from misago.users.models import Online, Rank
+from misago.users.online.utils import get_online_queryset
+
+
+RANKS_CACHE_NAME = 'misago_ranks_online'
+RANKS_CACHE_TIME = 3 * 60
+
+
+def get_ranks_online():
+    cached_online = cache.get(RANKS_CACHE_NAME, 'nada')
+    if cached_online == 'nada':
+        cached_online = get_ranks_from_db()
+        cache.set(RANKS_CACHE_NAME, cached_online, RANKS_CACHE_TIME)
+        return cached_online
+    else:
+        return cached_online
+
+
+def get_ranks_from_db():
+    _displayed_ranks = []
+
+    ranks_dict = {}
+    for rank in Rank.objects.filter(is_on_index=True).order_by('order'):
+        ranks_dict[rank.pk] = {
+            'id': rank.id,
+            'pk': rank.pk,
+            'name': rank.name,
+            'slug': rank.slug,
+            'description': rank.description,
+            'title': rank.title,
+            'css_class': rank.css_class,
+            'online': []
+        }
+        _displayed_ranks.append(ranks_dict[rank.pk])
+
+    queryset = get_online_queryset().filter(is_visible_on_index=True)
+    for tracker in queryset.iterator():
+        if tracker.user.rank_id in ranks_dict:
+            ranks_dict[tracker.user.rank_id]['online'].append({
+                'id': tracker.user.id,
+                'pk': tracker.user.pk,
+                'username': tracker.user.username,
+                'slug': tracker.user.slug,
+                'title': tracker.user.title,
+            })
+
+    ranks_online = []
+    for rank in _displayed_ranks:
+        if rank['online']:
+            ranks_online.append(rank)
+    return ranks_online
+
+
+def clear_ranks_online_cache():
+    cache.delete(RANKS_CACHE_NAME)

+ 45 - 0
misago/users/online/tracker.py

@@ -0,0 +1,45 @@
+from django.utils import timezone
+
+from misago.users.models import Online
+from misago.users.online.ranks import clear_ranks_online_cache
+
+
+def start_tracking(request, user):
+    online_tracker = Online.objects.create(
+        user=user,
+        current_ip=request._misago_real_ip,
+        is_visible_on_index=user.rank.is_on_index
+    )
+
+    if online_tracker.is_visible_on_index:
+        clear_ranks_online_cache()
+
+    request.user.online_tracker = online_tracker
+    request._misago_online_tracker = online_tracker
+
+
+def update_tracker(request, tracker):
+    tracker.current_ip = request._misago_real_ip
+    tracker.last_click = timezone.now()
+
+    rank_visible_on_index = request.user.rank.is_on_index
+    if tracker.is_visible_on_index != rank_visible_on_index:
+        tracker.is_visible_on_index = rank_visible_on_index
+        tracker.save(update_fields=[
+            'last_click', 'current_ip', 'is_visible_on_index'
+        ])
+        clear_ranks_online_cache()
+    else:
+        tracker.save(update_fields=['last_click', 'current_ip'])
+
+
+def stop_tracking(request, tracker):
+    user = tracker.user
+    user.last_login = tracker.last_click
+    user.last_ip = tracker.current_ip
+    user.save(update_fields=['last_login', 'last_ip'])
+
+    if tracker.is_visible_on_index:
+        clear_ranks_online_cache()
+
+    tracker.delete()

+ 3 - 4
misago/users/online.py → misago/users/online/utils.py

@@ -9,18 +9,17 @@ from misago.users.models import Online
 ACTIVITY_CUTOFF = timedelta(minutes=15)
 
 
-def get_online_queryset(viewer):
+def get_online_queryset(viewer=None):
     min_last_click = timezone.now() - ACTIVITY_CUTOFF
     queryset = Online.objects.filter(last_click__gte=min_last_click)
 
-    if not viewer.acl['can_see_hidden_users']:
+    if viewer and not viewer.acl['can_see_hidden_users']:
         queryset = queryset.filter(user__is_hiding_presence=False)
 
     return queryset.select_related('user')
 
 
-
-def state_for_acl(user, acl):
+def get_user_state(user, acl):
     user_state = {
         'is_banned': False,
         'is_hidden': user.is_hiding_presence,

+ 2 - 2
misago/users/views/lists.py

@@ -10,7 +10,7 @@ from django.views.decorators.cache import cache_page
 from misago.core.shortcuts import get_object_or_404, paginate
 
 from misago.users.models import Rank
-from misago.users.online import get_online_queryset
+from misago.users.online.utils import get_online_queryset
 from misago.users.permissions.profiles import (allow_browse_users_list,
                                                allow_see_users_online_list)
 from misago.users.sites import users_list
@@ -23,7 +23,7 @@ def render(request, template, context):
         page['reversed_link'] = reverse(page['link'])
 
     active_rank = context.get('rank')
-    for rank in Rank.objects.filter(is_tab=True).order_by('name'):
+    for rank in Rank.objects.filter(is_tab=True).order_by('order'):
         context['pages'].append({
             'name': rank.name,
             'reversed_link': reverse('misago:users_rank',

+ 2 - 2
misago/users/views/profile.py

@@ -10,9 +10,9 @@ from misago.core.decorators import require_POST
 from misago.core.shortcuts import get_object_or_404, paginate, validate_slug
 from misago.core.utils import clean_return_path
 
-from misago.users import online
 from misago.users.bans import get_user_ban
 from misago.users.decorators import deny_guests
+from misago.users.online.utils import get_user_state
 from misago.users.permissions.profiles import (allow_follow_user,
                                                allow_block_user)
 from misago.users.sites import user_profile
@@ -82,7 +82,7 @@ def render(request, template, context):
     else:
         context['show_email'] = False
 
-    context['state'] = online.state_for_acl(context['profile'], user_acl)
+    context['state'] = get_user_state(context['profile'], user_acl)
 
     return django_render(request, template, context)