Browse Source

Users lists

Rafał Pitoń 10 years ago
parent
commit
4ce2f5d012

+ 7 - 0
misago/conf/defaults.py

@@ -240,6 +240,13 @@ MISAGO_AVATARS_SIZES = (400, 200, 150, 100, 64, 50, 30, 20)
 MISAGO_AVATAR_SERVER_PATH = '/user-avatar'
 
 
+# Controls amount of data Misago has to process to build users rankings
+# Used for active posters and most liked users lists
+# The more active the forum, the shorter this period should be
+# Number of days
+MISAGO_RANKING_LENGTH = 30
+
+
 # X-Sendfile
 # X-Sendfile is feature provided by Http servers that allows web apps to
 # delegate serving files over to the better performing server instead of

+ 3 - 2
misago/core/templatetags/misago_batch.py

@@ -19,7 +19,8 @@ def batch(items, size):
             batch_size = 0
             batch_items = []
 
-    yield batch_items
+    if batch_items:
+        yield batch_items
 
 
 @register.filter
@@ -38,4 +39,4 @@ def batchnonefilled(items, size):
 
     if batch_size:
         batch_items.extend([None] * (size - batch_size))
-    yield batch_items
+        yield batch_items

+ 14 - 2
misago/static/misago/css/misago/header.less

@@ -13,6 +13,11 @@
   margin-bottom: @line-height-computed;
   padding: @line-height-computed 0px;
 
+  &.append-tabs {
+    border-bottom: 0px;
+    padding-bottom: @line-height-computed / 2;
+  }
+
   &>.container {
     h1 {
       margin: 0px;
@@ -193,12 +198,19 @@
 }
 
 .page-tabs {
-  border-bottom: 2px solid @page-header-border-color;
+  border-bottom: 3px solid @page-header-border-color;
   margin: @line-height-computed * -1 0px @line-height-computed;
   padding: 0px;
 
+  &.solo-right {
+    .nav-tabs {
+      margin-top: @line-height-computed * -1.5;
+      float: right;
+    }
+  }
+
   .nav-tabs {
     margin: 0px;
-    margin-bottom: -2px;
+    margin-bottom: -3px;
   }
 }

+ 20 - 10
misago/static/misago/css/misago/navbar.less

@@ -10,9 +10,10 @@
   margin-bottom: 0px;
 
   .navbar-brand {
-    .navbar-vertical-align(@navbar-height - (@navbar-brand-size - @font-size-large) / 2 + 1);
+    .navbar-vertical-align(@navbar-height - (@navbar-brand-size - @font-size-large) / 2 + 2);
+    margin-top: -1px;
     position: relative;
-    bottom: 1px;
+    top: 3px;
 
     font-size: @navbar-brand-size;
   }
@@ -41,7 +42,7 @@
 /* Big displays */
 @media (min-width: @screen-sm-min) {
   .navbar-nav-primary {
-    .navbar-vertical-align(@navbar-icon-size * 1.2);
+    .navbar-vertical-align(@navbar-icon-size * 1.7 - 2px);
     margin-bottom: 0px;
     padding-right: @navbar-padding-horizontal;
 
@@ -52,25 +53,30 @@
 
       &>a {
         /* Make button blocky */
+        border: 1px solid @navbar-icon-link-border;
         border-radius: @border-radius-base;
         margin: 0px;
         padding: 0px;
         padding-top: 6px;
-        height: @navbar-icon-size * 1.5;
-        width: @navbar-icon-size * 1.5;
+        height: @navbar-icon-size * 1.7;
+        width: @navbar-icon-size * 1.7;
 
         text-align: center;
 
-        &>.glyphicon, &>.fa{
-          font-size: @navbar-icon-size;
-          text-align: center;
-        }
-
         /* Hide item labels on big displays */
         .item-label {
           display: none;
         }
 
+        .glyphicon, .fa {
+          font-size: @navbar-icon-size;
+
+          &.fa-search {
+            position: relative;
+            bottom: 1px;
+          }
+        }
+
         &>.badge {
           position: absolute;
           right: -6px;
@@ -86,12 +92,16 @@
 
         &:hover {
           background-color: @navbar-icon-link-hover-bg !important;
+          border-color: @navbar-icon-link-hover-border;
+          transition-duration: 0.3ms;
 
           color: @navbar-icon-link-hover-color !important;
         }
 
         &:active {
           background-color: @navbar-icon-link-active-bg !important;
+          border-color: @navbar-icon-link-active-border;
+          transition-duration: 0.05ms;
 
           color: @navbar-icon-link-active-color !important;
         }

+ 0 - 2
misago/static/misago/css/misago/userslists.less

@@ -7,8 +7,6 @@
 //
 //==
 .users-cards {
-  margin-bottom: @line-height-computed * -1;
-
   .row {
     margin-bottom: @line-height-computed;
 

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

@@ -74,25 +74,25 @@
 @navbar-icon-size:                      @navbar-height / 2.5;
 @navbar-icon-link-bg:                   transparent;
 @navbar-icon-link-border:               transparent;
-@navbar-icon-link-color:                #999;
+@navbar-icon-link-color:                @state-default;
 
-@navbar-icon-link-hover-bg:             transparent;
-@navbar-icon-link-hover-border:         transparent;
-@navbar-icon-link-hover-color:          #333;
+@navbar-icon-link-hover-bg:             darken(@navbar-default-border, 2%);
+@navbar-icon-link-hover-border:         darken(@navbar-default-border, 2%);
+@navbar-icon-link-hover-color:          @state-hover;
 
-@navbar-icon-link-active-bg:            lighten(@navbar-default-bg, 5%);
-@navbar-icon-link-active-border:        transparent;
-@navbar-icon-link-active-color:         #333;
+@navbar-icon-link-active-bg:            darken(@navbar-default-border, 2%);
+@navbar-icon-link-active-border:        darken(@navbar-default-border, 2%);
+@navbar-icon-link-active-color:         @state-clicked;
 
 
 // Only User dropdown switch uses those colors
 @navbar-user-name-color:                @gray;
 @navbar-user-name-hover-color:          @navbar-user-name-color;
-@navbar-user-name-active-color:         @navbar-user-name-color;
+@navbar-user-name-active-color:         @state-clicked;
 
-@navbar-user-caret-color:              darken(@navbar-default-bg, 20%);
-@navbar-user-caret-hover-color:        darken(@navbar-default-bg, 40%);
-@navbar-user-caret-active-color:       @text-color;
+@navbar-user-caret-color:              @state-default;
+@navbar-user-caret-hover-color:        @state-hover;
+@navbar-user-caret-active-color:       @state-clicked;
 
 
 // Guest Sign in button

+ 14 - 0
misago/templates/misago/navbar.html

@@ -22,6 +22,20 @@
         <a href="{% url 'misago:register' %}" class="btn btn-register navbar-btn">{% trans "Register" %}</a>
       </div>
       {% endif %}
+
+      <ul class="nav navbar-nav navbar-nav-primary navbar-right">
+        <li>
+          <a href="#" class="tooltip-bottom" title="{% trans "Advanced search" %}">
+            <span class="fa fa-search fa-fw"></span>
+          </a>
+        </li>
+        <li>
+          <a href="{% url 'misago:users' %}" class="tooltip-bottom" title="{% trans "Users" %}">
+            <span class="fa fa-users fa-fw"></span>
+          </a>
+        </li>
+      </ul>
+
     </div><!-- /.navbar-collapse -->
   </div><!-- /.container -->
 </nav>

+ 4 - 4
misago/templates/misago/profile/follows.html

@@ -7,15 +7,15 @@
   <p class="lead">
     {% if is_authenticated_user %}
       {% blocktrans trimmed with follows=profile.following|intcomma count counter=profile.following %}
-      You are followig {{ follows }} user.
+      You are following {{ follows }} user.
       {% plural %}
-      You are followig {{ follows }} users.
+      You are following {{ follows }} users.
       {% endblocktrans %}
     {% else %}
       {% blocktrans trimmed with user=profile.username follows=profile.following|intcomma count counter=profile.following %}
-      {{ user }} is followig {{ follows }} user.
+      {{ user }} is following {{ follows }} user.
       {% plural %}
-      {{ user }} is followig {{ follows }} users.
+      {{ user }} is following {{ follows }} users.
       {% endblocktrans %}
     {% endif %}
   </p>

+ 2 - 2
misago/templates/misago/profile/pagination.html

@@ -17,11 +17,11 @@
   {% endif %}
   {% if page.has_next %}
     <li class="pull-right">
-      <a href="{% url link_name user_slug=user_slug user_id=user_id page=page.paginator.num_pages %}" class="tooltip-top" title="{% trans "Go to last page" %}">
+      <a href="{% url link_name user_slug=user_slug user_id=user_id page=paginator.num_pages %}" class="tooltip-top" title="{% trans "Go to last page" %}">
         {% trans "Last" %}
       </a>
     </li>
-    {% if page.next_page_number < page.paginator.num_pages %}
+    {% if page.next_page_number < paginator.num_pages %}
     <li class="pull-right">
       <a href="{% url link_name user_slug=user_slug user_id=user_id page=page.next_page_number %}" class="tooltip-top" title="{% trans "Go to next page" %}">
         {% trans "Next" %}

+ 42 - 0
misago/templates/misago/userslists/active_posters.html

@@ -0,0 +1,42 @@
+{% extends "misago/userslists/base.html" %}
+{% load humanize i18n %}
+
+
+{% block title %}{% trans "Active posters" %}{% endblock title %}
+
+
+{% block meta_description %}{% blocktrans trimmed with days=tracked_period %}
+List of users that have posted new messages during last {{ days }} days.
+{% endblocktrans %}{% endblock %}
+
+
+{% block users-list %}
+<p class="lead">
+  {% blocktrans trimmed with posters=users.paginator.count|intcomma days=tracked_period count counter=users.paginator.count %}
+  {{ posters }} user has posted new messages posting during last {{ days }} days.
+  {% plural %}
+  {{ posters }} users posted new messages during last {{ days }} days.
+  {% endblocktrans %}
+</p>
+
+{{ block.super }}
+{% endblock users-list %}
+
+
+{% block user-card-footer %}
+{{ block.super }}
+<small>
+  {% blocktrans trimmed with posts=user.followers|intcomma count counter=user.followers %}
+  {{ posts }} post
+  {% plural %}
+  {{ posts }} posts
+  {% endblocktrans %}
+</small>
+{% endblock user-card-footer %}
+
+
+{% block empty-list %}
+{% blocktrans trimmed with days=tracked_period %}
+No users have posted any new messages during last {{ days }} days.
+{% endblocktrans %}
+{% endblock empty-list %}

+ 70 - 0
misago/templates/misago/userslists/base.html

@@ -0,0 +1,70 @@
+{% extends "misago/base.html" %}
+{% load i18n misago_avatars misago_batch misago_pagination %}
+
+
+{% block title %}{{ active_page.name }}{% if users.page > 1 %} ({% blocktrans with page=users.page %}page {{ page }}{% endblocktrans %}){% endif %} | {{ block.super }}{% endblock title %}
+
+
+{% block content %}
+<div class="page-header append-tabs">
+  <div class="container">
+
+    <h1>
+      <span class="fa fa-users"></span>
+      {% trans "Users" %}
+    </h1>
+
+  </div>
+</div>
+<div class="page-tabs solo-right">
+  <div class="container">
+    <ul class="nav nav-tabs">
+      {% for page in pages %}
+      <li {% if page.is_active %}class="active"{% endif %}>
+        <a href="{{ page.reversed_link }}">
+          {{ page.name}}
+        </a>
+      </li>
+      {% endfor %}
+    </ul>
+  </div>
+</div>
+
+<div class="container">
+  {% if users.object_list %}
+    {% block users-list %}
+    <div class="users-cards">
+      {% for row in users.object_list|batch:6 %}
+      <div class="row">
+        {% for card in row %}
+        <div class="col-md-2">
+
+          {% block user-card %}
+          <a href="{% url USER_PROFILE_URL user_slug=card.slug user_id=card.id %}" class="user-card {% if card.rank.css_class %}card-{{ card.rank.css_class }}{% endif %}">
+            <img src="{{ card|avatar:400 }}" alt="{% trans "Avatar" %}">
+            <div class="card-footer">
+              {% block user-card-footer %}
+              <h4 class="user-name">{{ card.username }}</h4>
+              {% endblock user-card-footer %}
+            </div>
+          </a>
+          {% endblock user-card %}
+
+        </div>
+        {% endfor %}
+      </div>
+      {% endfor %}
+    </div>
+    {% endblock users-list %}
+
+    {% block list-pagination %}
+      {% pagination users "misago/userslists/pagination.html" active_page.link %}
+    {% endblock list-pagination %}
+  {% else %}
+    <p class="lead">
+      {% block empty-list %}
+      {% endblock empty-list %}
+    </p>
+  {% endif %}
+</div>
+{% endblock content%}

+ 41 - 0
misago/templates/misago/userslists/online.html

@@ -0,0 +1,41 @@
+{% extends "misago/userslists/base.html" %}
+{% load humanize i18n misago_avatars misago_batch %}
+
+
+{% block title %}{% trans "Users online" %}{% endblock title %}
+
+
+{% block meta_description %}{% trans "List of signed in users currently browsing forums." %}{% endblock %}
+
+
+{% block users-list %}
+<p class="lead">
+  {% blocktrans trimmed with online=users.paginator.count|intcomma count counter=users.paginator.count %}
+  {{ online }} user is currently online.
+  {% plural %}
+  {{ online }} users are currently online.
+  {% endblocktrans %}
+</p>
+
+{{ block.super }}
+{% endblock users-list %}
+
+
+{% block user-card %}
+<a href="{% url USER_PROFILE_URL user_slug=card.user.slug user_id=card.user.id %}" class="user-card {% if card.user.rank.css_class %}card-{{ card.user.rank.css_class }}{% endif %}">
+  <img src="{{ card.user|avatar:400 }}" alt="{% trans "Avatar" %}">
+  <div class="card-footer">
+    <h4 class="user-name">{{ card.user.username }}</h4>
+    <small>
+      <abbr class="tooltip-top dynamic time-ago" title="{% blocktrans with last_click=card.last_click|date:"TIME_FORMAT" %}Last click on {{ last_click }}{% endblocktrans %}" data-timestamp="{{ card.last_click|date:"c" }}">
+        {{ card.last_click|date }}
+      </abbr>
+    </small>
+  </div>
+</a>
+{% endblock user-card %}
+
+
+{% block empty-list %}
+{% trans "No registered users are signed in or you can't see them." %}
+{% endblock empty-list %}

+ 33 - 0
misago/templates/misago/userslists/pagination.html

@@ -0,0 +1,33 @@
+{% load i18n %}
+{% if paginator.num_pages > 1 %}
+<ul class="pager pager-wide">
+  {% if page.has_previous %}
+    <li class="pull-left">
+      <a href="{% url link_name %}" class="tooltip-top" title="{% trans "Go to first page" %}">
+        {% trans "Start" %}
+      </a>
+    </li>
+    {% if page.number > 2 %}
+    <li class="pull-left">
+      <a href="{% url link_name page=page.previous_page_number %}" class="tooltip-top" title="{% trans "Go to previous page" %}">
+        {% trans "Previous" %}
+      </a>
+    </li>
+    {% endif %}
+  {% endif %}
+  {% if page.has_next %}
+    <li class="pull-right">
+      <a href="{% url link_name page=paginator.num_pages %}" class="tooltip-top" title="{% trans "Go to last page" %}">
+        {% trans "Last" %}
+      </a>
+    </li>
+    {% if page.next_page_number < paginator.num_pages %}
+    <li class="pull-right">
+      <a href="{% url link_name page=page.next_page_number %}" class="tooltip-top" title="{% trans "Go to next page" %}">
+        {% trans "Next" %}
+      </a>
+    </li>
+    {% endif %}
+  {% endif %}
+</ul>
+{% endif %}

+ 76 - 0
misago/templates/misago/userslists/rank.html

@@ -0,0 +1,76 @@
+{% extends "misago/userslists/base.html" %}
+{% load humanize i18n %}
+
+
+{% block title %}{{ active_page.name }}{% endblock title %}
+
+
+{% block meta_description %}{% blocktrans trimmed with rank=rank.name users=users.paginator.count|intcomma count counter=users.paginator.count %}
+There is {{ users }} user with {{ rank }} rank.
+{% plural %}
+There are {{ users }} users with {{ rank }} rank.
+{% endblocktrans %}{% if rank.description %}
+{{ rank.description|escape|urlize|linebreaks }}{% endif %}{% endblock %}
+
+
+{% block users-list %}
+{% if rank.description %}
+<p class="lead">
+  {{ rank.description|escape|urlize|linebreaks }}
+</p>
+{% endif %}
+
+<p class="lead">
+{% blocktrans trimmed with rank=rank.name users=users.paginator.count|intcomma count counter=users.paginator.count %}
+  There is {{ users }} user with {{ rank }} rank.
+{% plural %}
+  There are {{ users }} users with {{ rank }} rank.
+{% endblocktrans %}
+</p>
+
+{{ block.super }}
+{% endblock users-list %}
+
+
+{% block list-pagination %}
+{% if users.paginator.num_pages > 1 %}
+<ul class="pager pager-wide">
+  {% if users.has_previous %}
+    <li class="pull-left">
+      <a href="{% url 'misago:users_rank' rank_slug=rank.slug %}" class="tooltip-top" title="{% trans "Go to first page" %}">
+        {% trans "Start" %}
+      </a>
+    </li>
+    {% if users.number > 2 %}
+    <li class="pull-left">
+      <a href="{% url 'misago:users_rank' rank_slug=rank.slug page=users.previous_page_number %}" class="tooltip-top" title="{% trans "Go to previous page" %}">
+        {% trans "Previous" %}
+      </a>
+    </li>
+    {% endif %}
+  {% endif %}
+  {% if users.has_next %}
+    <li class="pull-right">
+      <a href="{% url 'misago:users_rank' rank_slug=rank.slug page=users.paginator.num_pages %}" class="tooltip-top" title="{% trans "Go to last page" %}">
+        {% trans "Last" %}
+      </a>
+    </li>
+    {% if users.next_page_number < users.paginator.num_pages %}
+    <li class="pull-right">
+      <a href="{% url 'misago:users_rank' rank_slug=rank.slug page=users.next_page_number %}" class="tooltip-top" title="{% trans "Go to next page" %}">
+        {% trans "Next" %}
+      </a>
+    </li>
+    {% endif %}
+  {% endif %}
+</ul>
+{% endif %}
+{% endblock list-pagination %}
+
+
+{% block user-card-footer %}
+{{ block.super }}
+{% if card.title %}
+<small>{{ card.title }}</small>
+{% endif %}
+{% endblock user-card-footer %}

+ 4 - 3
misago/users/apps.py

@@ -35,9 +35,10 @@ class MisagoUsersConfig(AppConfig):
                         icon='fa fa-ticket')
 
     def register_default_users_list_pages(self):
-        users_list.add_page(link='misago:index',
-                            name='Todo',
-                            icon='fa fa-check')
+        users_list.add_page(link='misago:users_active_posters',
+                            name=_('Active posters'))
+        users_list.add_page(link='misago:users_online',
+                            name=_('Online'))
 
     def register_default_user_profile_pages(self):
         def posts_badge(request, profile):

+ 11 - 0
misago/users/online.py

@@ -9,6 +9,17 @@ from misago.users.models import Online
 ACTIVITY_CUTOFF = timedelta(minutes=15)
 
 
+def get_online_queryset(viewer):
+    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']:
+        queryset = queryset.filter(user__is_hiding_presence=False)
+
+    return queryset.select_related('user', 'user__rank')
+
+
+
 def state_for_acl(user, acl):
     user_state = {
         'is_banned': False,

+ 13 - 0
misago/users/urls.py

@@ -53,6 +53,19 @@ urlpatterns += patterns('misago.users.views.usercp',
 
 
 urlpatterns += patterns('',
+    url(r'^users/', include(patterns('misago.users.views.lists',
+        url(r'^$', 'lander', name="users"),
+        url(r'^active-posters/$', 'active_posters', name="users_active_posters"),
+        url(r'^active-posters/(?P<page>\d+)/$', 'active_posters', name="users_active_posters"),
+        url(r'^online/$', 'online', name="users_online"),
+        url(r'^online/(?P<page>\d+)/$', 'online', name="users_online"),
+        url(r'^(?P<rank_slug>[-a-zA-Z0-9]+)/$', 'rank', name="users_rank"),
+        url(r'^(?P<rank_slug>[-a-zA-Z0-9]+)/(?P<page>\d+)/$', 'rank', name="users_rank"),
+    )))
+)
+
+
+urlpatterns += patterns('',
     url(r'^user/(?P<user_slug>[a-zA-Z0-9]+)-(?P<user_id>\d+)/', include(patterns('misago.users.views.profile',
         url(r'^$', 'posts', name="user_posts"),
         url(r'^threads/$', 'threads', name="user_threads"),

+ 70 - 0
misago/users/views/lists.py

@@ -0,0 +1,70 @@
+from django.conf import settings
+from django.contrib.auth import get_user_model
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect, render as django_render
+
+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.sites import users_list
+
+
+def lander(request):
+    default = users_list.get_default_link()
+    return redirect(default)
+
+
+def render(request, template, context):
+    context['pages'] = users_list.get_pages(request)
+
+    for page in context['pages']:
+        page['reversed_link'] = reverse(page['link'])
+
+    active_rank = context.get('rank')
+    for rank in Rank.objects.filter(is_tab=True).order_by('name'):
+        context['pages'].append({
+            'name': rank.name,
+            'reversed_link': reverse('misago:users_rank',
+                                     kwargs={'rank_slug': rank.slug}),
+            'is_active': active_rank.pk == rank.pk if active_rank else None
+        })
+
+    for page in context['pages']:
+        if page['is_active']:
+            context['active_page'] = page
+            break
+
+    return django_render(request, template, context)
+
+
+def list_view(request, template, queryset, page, context=None):
+    context = context or {}
+    context['users'] = paginate(queryset, page, 6 * 3, 5)
+    return render(request, template, context)
+
+
+def active_posters(request, page=0):
+    tracked_period = settings.MISAGO_RANKING_LENGTH
+    User = get_user_model()
+    queryset = User.objects.all()
+
+    template =  "misago/userslists/active_posters.html"
+    return list_view(request, template, queryset, page, {
+        'tracked_period': tracked_period
+    })
+
+
+def online(request, page=0):
+    queryset = get_online_queryset(request.user).order_by('user__slug')
+
+    template =  "misago/userslists/online.html"
+    return list_view(request, template, queryset, page)
+
+
+def rank(request, rank_slug, page=0):
+    rank = get_object_or_404(Rank.objects.filter(is_tab=True), slug=rank_slug)
+    queryset = rank.user_set.order_by('slug')
+
+    template =  "misago/userslists/rank.html"
+    return list_view(request, template, queryset, page, {'rank': rank})