Просмотр исходного кода

- Fancy rank styles
- "Team Online" replaced with more versalite solution
- Board Index template updated
- Mark all forums read button added
- Updated ranks fixture

Ralfp 12 лет назад
Родитель
Сommit
f585a4157f

+ 2 - 1
misago/authn/decorators.py

@@ -1,10 +1,10 @@
 from django.utils.translation import ugettext as _
-from misago.views import error403
 
 def block_authenticated(f):
     def decorator(*args, **kwargs):
         request = args[0]
         if not request.firewall.admin and request.user.is_authenticated():
+            from misago.views import error403
             return error403(request, _("%(username)s, this page is not available to signed in users.") % {'username': request.user.username})
         return f(*args, **kwargs)
     return decorator
@@ -14,6 +14,7 @@ def block_guest(f):
     def decorator(*args, **kwargs):
         request = args[0]
         if not request.user.is_authenticated():
+            from misago.views import error403
             return error403(request, _("Dear Guest, only signed in members are allowed to access this page. Please sign in or register and try again."))
         return f(*args, **kwargs)
     return decorator

+ 1 - 1
misago/csrf/decorators.py

@@ -1,10 +1,10 @@
 from django.utils.translation import ugettext as _
-from misago.views import error403
 
 def check_csrf(f):
     def decorator(*args, **kwargs):
         request = args[0]
         if not request.csrf.request_secure(request):
+            from misago.views import error403
             return error403(request, _("Request authorization is invalid. Please try again."))
         return f(*args, **kwargs)
     return decorator

+ 35 - 34
misago/ranks/fixtures.py

@@ -3,41 +3,42 @@ from misago.utils import ugettext_lazy as _
 from misago.utils import get_msgid
 
 def load_fixtures():
-    Rank.create(
-                name=_("Forum Team").message,
-                name_slug='forum_team',
-                title=_("Forum Team").message,
-                style='rank-team',
-                special=True,
-                order=0,
-                as_tab=True,
-                )
+    Rank.objects.create(
+                        name=_("Forum Team").message,
+                        name_slug='forum_team',
+                        title=_("Forum Team").message,
+                        style='rank-team',
+                        special=True,
+                        order=0,
+                        as_tab=True,
+                        on_index=True,
+                        )
     
-    Rank.create(
-                name=_("Most Valueable Posters").message,
-                title=_("MVP").message,
-                style='rank-mpv',
-                special=True,
-                order=1,
-                as_tab=True,
-                )
+    Rank.objects.create(
+                        name=_("Most Valueable Posters").message,
+                        title=_("MVP").message,
+                        style='rank-mvp',
+                        special=True,
+                        order=1,
+                        as_tab=True,
+                        )
     
-    Rank.create(
-                name=_("Lurkers").message,
-                order=1,
-                criteria="100%"
-                )
+    Rank.objects.create(
+                        name=_("Lurkers").message,
+                        order=1,
+                        criteria="100%"
+                        )
     
-    Rank.create(
-                name=_("Members").message,
-                order=2,
-                criteria="75%"
-                )
+    Rank.objects.create(
+                        name=_("Members").message,
+                        order=2,
+                        criteria="75%"
+                        )
     
-    Rank.create(
-                name=_("Active Members").message,
-                style='rank-active',
-                order=3,
-                criteria="10%",
-                as_tab=True,
-                )
+    Rank.objects.create(
+                        name=_("Active Members").message,
+                        style='rank-active',
+                        order=3,
+                        criteria="10%",
+                        as_tab=True,
+                        )

+ 3 - 1
misago/ranks/forms.py

@@ -14,6 +14,7 @@ class RankForm(Form):
     style = forms.CharField(max_length=255,required=False)
     special = forms.BooleanField(widget=YesNoSwitch,required=False)
     as_tab = forms.BooleanField(widget=YesNoSwitch,required=False)
+    on_index = forms.BooleanField(widget=YesNoSwitch,required=False)
     criteria = forms.CharField(max_length=255,initial='0',validators=[RegexValidator(regex='^(\d+)(%?)$',message=_('This is incorrect rank match rule.'))],required=False)
     
     layout = (
@@ -22,7 +23,8 @@ class RankForm(Form):
                (
                 ('name', {'label': _("Rank Name"), 'help_text': _("Rank Name is used to identify rank in Admin Control Panel and is used as page and tab title if you decide to make this rank act as tab on users list.")}),
                 ('description', {'label': _("Rank Description"), 'help_text': _("If this rank acts as tab on users list, here you can enter optional description that will be displayed above list of users with this rank.")}),
-                ('as_tab', {'label': _("As Tab"), 'help_text': _("Should this rank have its own page on users list, containing rank's description and list of users that have it? This is good option for rank used by forum team members or members that should be visible and easily reachable.")}),
+                ('as_tab', {'label': _("As Tab on Users List"), 'help_text': _("Should this rank have its own page on users list, containing rank's description and list of users that have it? This is good option for rank used by forum team members or members that should be visible and easily reachable.")}),
+                ('on_index', {'label': _("Display members online"), 'help_text': _("Should users online with this rank be displayed on board index?")}),
                )
               ),
               (

+ 1 - 0
misago/ranks/models.py

@@ -14,6 +14,7 @@ class Rank(models.Model):
     title = models.CharField(max_length=255,null=True,blank=True)
     special = models.BooleanField(default=False)
     as_tab = models.BooleanField(default=False)
+    on_index = models.BooleanField(default=False)
     order = models.IntegerField(default=0)
     criteria = models.CharField(max_length=255,null=True,blank=True)
     

+ 3 - 0
misago/ranks/views.py

@@ -89,6 +89,7 @@ class New(FormWidget):
                       title = form.cleaned_data['title'],
                       special = form.cleaned_data['special'],
                       as_tab = form.cleaned_data['as_tab'],
+                      on_index = form.cleaned_data['on_index'],
                       order = (last_rank.order + 1 if last_rank else 0),
                       criteria = form.cleaned_data['criteria']
                      )
@@ -120,6 +121,7 @@ class Edit(FormWidget):
                 'title': model.title,
                 'special': model.special,
                 'as_tab': model.as_tab,
+                'on_index': model.on_index,
                 'criteria': model.criteria
                 }
     
@@ -131,6 +133,7 @@ class Edit(FormWidget):
         target.title = form.cleaned_data['title']
         target.special = form.cleaned_data['special']
         target.as_tab = form.cleaned_data['as_tab']
+        target.on_index = form.cleaned_data['on_index']
         target.criteria = form.cleaned_data['criteria']
         target.save(force_update=True)
         return target, Message(_('Changes in rank "%(name)s" have been saved.') % {'name': self.original_name}, 'success')

+ 1 - 0
misago/sessions/models.py

@@ -10,6 +10,7 @@ class Session(models.Model):
     start = models.DateTimeField()
     last = models.DateTimeField()
     team = models.BooleanField(default=False)
+    rank = models.ForeignKey('ranks.Rank', related_name='sessions', null=True, on_delete=models.SET_NULL)
     admin = models.BooleanField(default=False)
     matched = models.BooleanField(default=False)
     hidden = models.BooleanField(default=False)

+ 2 - 0
misago/sessions/sessions.py

@@ -119,6 +119,7 @@ class SessionHuman(SessionMisago):
         self.expired = False
         self.hidden = False
         self.team = False
+        self.rank = None
         self.remember_me = None
         self._user = None
         self._ip = self.get_ip(request)
@@ -197,6 +198,7 @@ class SessionHuman(SessionMisago):
         self._session_rk.user = self._user
         self._session_rk.hidden = self.hidden
         self._session_rk.team = self.team
+        self._session_rk.rank_id = self.rank
         super(SessionHuman, self).save()
         
     def human_session(self):

+ 1 - 0
misago/urls.py

@@ -16,6 +16,7 @@ urlpatterns = patterns('',
     url(r'^redirect/(?P<slug>(\w|-)+)-(?P<forum>\d+)/$', 'misago.views.redirection', name="redirect"),
     url(r'^markdown/preview/$', 'misago.markdown.views.preview', name="md_preview"),
     url(r'^$', 'misago.views.home', name="index"),
+    url(r'^read/$', 'misago.views.read_all', name="read_all"),
 )
 
 # Include admin patterns

+ 5 - 3
misago/users/middleware.py

@@ -15,12 +15,14 @@ def set_timezone(new_tz):
 class UserMiddleware(object):
     def process_request(self, request):
         if request.user.is_authenticated():
-            # Set user timezone
+            # Set user timezone and rank
+            request.session.rank = request.user.rank_id
             set_timezone(request.user.timezone)
             
             # Display "welcome back!" message
             if request.session.remember_me:
                 request.messages.set_message(_("Welcome back, %(username)s! We've signed you in automatically for your convenience.") % {'username': request.user.username}, 'info')
         else:
-            # Set guest's timezone
-            set_timezone(request.settings['default_timezone'])
+            # Set guest's timezone and empty rank
+            set_timezone(request.settings['default_timezone'])
+            request.session.rank = None

+ 46 - 9
misago/views.py

@@ -2,9 +2,15 @@ from django.core.cache import cache
 from django.core.urlresolvers import reverse
 from django.shortcuts import redirect
 from django.template import RequestContext
+from django.utils import timezone
 from django.utils.translation import ugettext as _
+from misago.authn.decorators import block_guest
+from misago.csrf.decorators import check_csrf
 from misago.forums.models import Forum
+from misago.messages import Message
+from misago.readstracker.models import Record
 from misago.readstracker.trackers import ForumsTracker
+from misago.ranks.models import Rank
 from misago.sessions.models import Session
 from misago.threads.models import Thread
 
@@ -14,21 +20,36 @@ def home(request):
     if popular_threads == 'nada' and request.settings['thread_ranking_size'] > 0:
         popular_threads = []
         for thread in Thread.objects.filter(moderated=False).filter(deleted=False).filter(forum__in=request.acl.threads.get_readable_forums(request.acl)).prefetch_related('forum').order_by('-score')[:request.settings['thread_ranking_size']]:
+            thread.forum_name = thread.forum.name
+            thread.forum_slug = thread.forum.slug
             popular_threads.append(thread)
-        cache.set('thread_ranking_%s' % request.user.make_acl_key(), popular_threads, request.settings['thread_ranking_refresh'])  
-    # Team online
-    team_online = []
-    team_pks = []
-    for session in Session.objects.filter(team=1).filter(admin=0).filter(user__isnull=False).order_by('-start').select_related('user', 'user__rank'):
-        if session.user.pk not in team_pks:
-            team_pks.append(session.user.pk)
-            team_online.append(session.user)
+        cache.set('thread_ranking_%s' % request.user.make_acl_key(), popular_threads, request.settings['thread_ranking_refresh'])
+          
+    # Ranks online
+    ranks_list = cache.get('users_online', 'nada')
+    if ranks_list == 'nada':
+        ranks_dict = {}
+        ranks_list = []
+        users_list = []
+        for rank in Rank.objects.filter(on_index=True).order_by('order'):
+            rank_entry = {'name': rank.name, 'style': rank.style, 'title': rank.title, 'online': []}
+            ranks_list.append(rank_entry)
+            ranks_dict[rank.pk] = rank_entry
+        if ranks_dict:
+            for session in Session.objects.select_related('user').filter(rank__in=ranks_dict.keys()).filter(user__isnull=False):
+                if not session.user_id in users_list:
+                    ranks_dict[session.user.rank_id]['online'].append(session.user)
+                    users_list.append(session.user_id)
+            del ranks_dict
+            del users_list
+        cache.set('ranks_list', ranks_list, 15)
+            
     # Render page with forums list
     reads_tracker = ForumsTracker(request.user)
     return request.theme.render_to_response('index.html',
                                             {
                                              'forums_list': Forum.objects.treelist(request.acl.forums, tracker=reads_tracker),
-                                             'team_online': team_online,
+                                             'ranks_online': ranks_list,
                                              'popular_threads': popular_threads,
                                              },
                                             context_instance=RequestContext(request));
@@ -68,6 +89,22 @@ def redirection(request, forum, slug):
         return error404(request)
 
 
+@block_guest
+@check_csrf
+def read_all(request):
+    Record.objects.filter(user=request.user).delete()
+    now = timezone.now()
+    bulk = []
+    for forum in request.acl.forums.known_forums():
+        new_record = Record(user=request.user, forum_id=forum, updated=now, cleared=now)
+        new_record.set_threads({})
+        bulk.append(new_record)
+    if bulk:
+        Record.objects.bulk_create(bulk)
+    request.messages.set_flash(Message(_("All forums have been marked as read.")), 'success')
+    return redirect(reverse('index'))
+
+
 def redirect_message(request, message, type='info', owner=None):
     request.messages.set_flash(message, type, owner)
     return redirect(reverse('index'))

+ 71 - 0
static/sora/css/ranks.less

@@ -0,0 +1,71 @@
+// Ranks styles
+// -------------------------
+
+// .rank-team
+.well-post.rank-team {
+  border: 1px solid lighten(@linkColor, 5%);
+  .box-shadow(0px 0px 0px 3px lighten(@linkColor, 30%));
+}
+
+.team-online.rank-team ul {
+  li {
+    background-color: @linkColor;
+     
+    div {
+      a {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@blue, 40%);
+      }
+      
+      .muted {
+        color: darken(@blue, 25%);
+      }
+    }
+  }
+}
+
+// .rank-mvp
+.well-post.rank-mvp {
+  border: 1px solid lighten(@purple, 5%);
+  .box-shadow(0px 0px 0px 3px lighten(@purple, 30%));
+}
+
+.team-online.rank-mvp ul {
+  li {
+    background-color: lighten(@purple, 10%);
+     
+    div {
+      a {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@purple, 40%);
+      }
+      
+      .muted {
+        color: darken(@purple, 25%);
+      }
+    }
+  }
+}
+
+// .rank-active
+.well-post.rank-active {
+  border: 1px solid lighten(@orange, 5%);
+  .box-shadow(0px 0px 0px 3px lighten(@orange, 30%));
+}
+
+.team-online.rank-active ul {
+  li {
+    background-color: @orange;
+     
+    div {
+      a {
+        color: @white;
+        text-shadow: 0px 1px 0px darken(@orange, 40%);
+      }
+      
+      .muted {
+        color: darken(@orange, 25%);
+      }
+    }
+  }
+}

+ 22 - 4
static/sora/css/sora.css

@@ -826,9 +826,9 @@ a.label:hover,a.badge:hover{color:#ffffff;text-decoration:none;cursor:pointer;}
 .invisible{visibility:hidden;}
 .affix{position:fixed;}
 @media (min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:20px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px;} .span12{width:724px;} .span11{width:662px;} .span10{width:600px;} .span9{width:538px;} .span8{width:476px;} .span7{width:414px;} .span6{width:352px;} .span5{width:290px;} .span4{width:228px;} .span3{width:166px;} .span2{width:104px;} .span1{width:42px;} .offset12{margin-left:764px;} .offset11{margin-left:702px;} .offset10{margin-left:640px;} .offset9{margin-left:578px;} .offset8{margin-left:516px;} .offset7{margin-left:454px;} .offset6{margin-left:392px;} .offset5{margin-left:330px;} .offset4{margin-left:268px;} .offset3{margin-left:206px;} .offset2{margin-left:144px;} .offset1{margin-left:82px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%;} .row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%;} .row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%;} .row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%;} .row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%;} .row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%;} .row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%;} .row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%;} .row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%;} .row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%;} .row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%;} .row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%;} .row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%;} .row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%;} .row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%;} .row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%;} .row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%;} .row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%;} .row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%;} .row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%;} .row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%;} .row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%;} .row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%;} .row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%;} .row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%;} .row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%;} .row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%;} .row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%;} .row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%;} .row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%;} .row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%;} .row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%;} .row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%;} .row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%;} .row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:20px;} input.span12, textarea.span12, .uneditable-input.span12{width:710px;} input.span11, textarea.span11, .uneditable-input.span11{width:648px;} input.span10, textarea.span10, .uneditable-input.span10{width:586px;} input.span9, textarea.span9, .uneditable-input.span9{width:524px;} input.span8, textarea.span8, .uneditable-input.span8{width:462px;} input.span7, textarea.span7, .uneditable-input.span7{width:400px;} input.span6, textarea.span6, .uneditable-input.span6{width:338px;} input.span5, textarea.span5, .uneditable-input.span5{width:276px;} input.span4, textarea.span4, .uneditable-input.span4{width:214px;} input.span3, textarea.span3, .uneditable-input.span3{width:152px;} input.span2, textarea.span2, .uneditable-input.span2{width:90px;} input.span1, textarea.span1, .uneditable-input.span1{width:28px;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";line-height:0;} .row:after{clear:both;} [class*="span"]{float:left;min-height:1px;margin-left:30px;} .container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px;} .span12{width:1170px;} .span11{width:1070px;} .span10{width:970px;} .span9{width:870px;} .span8{width:770px;} .span7{width:670px;} .span6{width:570px;} .span5{width:470px;} .span4{width:370px;} .span3{width:270px;} .span2{width:170px;} .span1{width:70px;} .offset12{margin-left:1230px;} .offset11{margin-left:1130px;} .offset10{margin-left:1030px;} .offset9{margin-left:930px;} .offset8{margin-left:830px;} .offset7{margin-left:730px;} .offset6{margin-left:630px;} .offset5{margin-left:530px;} .offset4{margin-left:430px;} .offset3{margin-left:330px;} .offset2{margin-left:230px;} .offset1{margin-left:130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";line-height:0;} .row-fluid:after{clear:both;} .row-fluid [class*="span"]{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;float:left;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;} .row-fluid [class*="span"]:first-child{margin-left:0;} .row-fluid .span12{width:100%;*width:99.94680851063829%;} .row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%;} .row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%;} .row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%;} .row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%;} .row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%;} .row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%;} .row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%;} .row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%;} .row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%;} .row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%;} .row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%;} .row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%;} .row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%;} .row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%;} .row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%;} .row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%;} .row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%;} .row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%;} .row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%;} .row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%;} .row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%;} .row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%;} .row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%;} .row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%;} .row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%;} .row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%;} .row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%;} .row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%;} .row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%;} .row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%;} .row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%;} .row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%;} .row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%;} .row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%;} .row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%;} input,textarea,.uneditable-input{margin-left:0;} .controls-row [class*="span"]+[class*="span"]{margin-left:30px;} input.span12, textarea.span12, .uneditable-input.span12{width:1156px;} input.span11, textarea.span11, .uneditable-input.span11{width:1056px;} input.span10, textarea.span10, .uneditable-input.span10{width:956px;} input.span9, textarea.span9, .uneditable-input.span9{width:856px;} input.span8, textarea.span8, .uneditable-input.span8{width:756px;} input.span7, textarea.span7, .uneditable-input.span7{width:656px;} input.span6, textarea.span6, .uneditable-input.span6{width:556px;} input.span5, textarea.span5, .uneditable-input.span5{width:456px;} input.span4, textarea.span4, .uneditable-input.span4{width:356px;} input.span3, textarea.span3, .uneditable-input.span3{width:256px;} input.span2, textarea.span2, .uneditable-input.span2{width:156px;} input.span1, textarea.span1, .uneditable-input.span1{width:56px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;} .row-fluid .thumbnails{margin-left:0;}}.breadcrumb .active{color:#333333;}
-.breadcrumb.bottom{margin-top:24px;margin-bottom:0px;}
+.breadcrumb.bottom{background-color:#e3e3e3;margin-top:24px;margin-bottom:0px;}
 .page-header .breadcrumb{background:none;padding:0px;margin-bottom:0px;}
-footer{padding-top:12px;padding-bottom:32px;color:#b0b0b0;}footer a,footer a:link,footer a:active,footer a:visited{color:#b0b0b0;text-decoration:underline;}
+footer{padding-top:12px;padding-bottom:32px;color:#a3a3a3;}footer a,footer a:link,footer a:active,footer a:visited{color:#b0b0b0;text-decoration:underline;}
 footer a:hover{color:#7d7d7d;}
 footer .go-to-top{float:right;}footer .go-to-top,footer .go-to-top:link,footer .go-to-top:active,footer .go-to-top:visited{text-decoration:none;}footer .go-to-top i,footer .go-to-top:link i,footer .go-to-top:active i,footer .go-to-top:visited i{opacity:0.4;filter:alpha(opacity=40);}
 footer .go-to-top:hover i{opacity:0.65;filter:alpha(opacity=65);}
@@ -948,7 +948,19 @@ td.lead-cell{color:#555555;font-weight:bold;}
 .nav-tabs .tab-search form{marging:0px;margin-bottom:-4px;}
 .nav-tabs .tab-search.tab-search-no-tabs{position:relative;bottom:12px;}
 .nav-tabs button{padding-left:7px;padding-right:7px;}
+.list-empty{margin-top:32px;font-size:180%;}
+.index-block{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;padding:8px;margin-top:8px;margin-bottom:24px;}.index-block h3{border-bottom:1px solid #bfbfbf;color:#999999;font-size:120%;margin-top:-6px;padding-top:0px;}
+.index-block ul{margin:0px;margin-top:-10px;padding:0px;}.index-block ul li{border-bottom:1px solid #d9d9d9;margin:0px;overflow:auto;}
+.team-online{margin-bottom:0px;padding-bottom:0px;}.team-online h3{border-bottom:none;margin-bottom:0px;color:#c8c8c8;}
+.team-online ul{margin:0px -6px;}.team-online ul li{background-color:#eeeeee;border:none;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;margin-bottom:8px;padding:6px;}
+.team-online div{float:left;position:relative;top:4px;font-weight:bold;}.team-online div a{display:block;color:#333333;font-size:160%;}
+.team-online div .muted{color:#999999;}
+.team-online img{float:left;margin-right:12px;width:48px;height:48px;}
+.thread-ranking{margin-bottom:8px;}.thread-ranking li{padding:8px 0px;}
+.thread-ranking a{color:#999999;}.thread-ranking a.lead{display:block;margin:0px;margin-bottom:-4px;padding:0px;color:#333333;font-size:120%;font-weight:bold;}
+.forum-stats{color:#b3b3b3;}.forum-stats strong{padding-left:8px;color:#555555;font-size:170%;}
 .forums-list{padding-top:4px;}.forums-list .category h2{color:#666666;font-size:110%;margin-bottom:0px;}.forums-list .category h2 small{color:#a6a6a6;font-size:100%;}
+.forums-list .category h2 .form-inline{float:right;margin:0px;padding:0px;}.forums-list .category h2 .form-inline .btn-link{opacity:0.25;filter:alpha(opacity=25);}.forums-list .category h2 .form-inline .btn-link:hover,.forums-list .category h2 .form-inline .btn-link:active{background-color:#dc4e44;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;opacity:1;filter:alpha(opacity=100);color:#ffffff;}.forums-list .category h2 .form-inline .btn-link:hover i,.forums-list .category h2 .form-inline .btn-link:active i{background-image:url("../img/glyphicons-halflings-white.png");}
 .forums-list .category-important .well-forum{border:1px solid #0099e6;-webkit-box-shadow:0px 0px 0px 3px #66ccff;-moz-box-shadow:0px 0px 0px 3px #66ccff;box-shadow:0px 0px 0px 3px #66ccff;}
 .forums-list .well-forum{margin-bottom:14px;overflow:auto;padding:12px 8px;padding-bottom:8px;}.forums-list .well-forum .row .span3{margin-left:0px;padding-left:16px;}
 .forums-list .well-forum .forum-icon{background-color:#eeeeee;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;float:left;padding:3px 6px;position:relative;bottom:4px;margin-bottom:-4px;}
@@ -1017,6 +1029,12 @@ td.lead-cell{color:#555555;font-weight:bold;}
 .profile-header .avatar-height .lead{color:#555555;}.profile-header .avatar-height .lead .muted{color:#959595;}
 .profile-header .nav-tabs{margin-top:-22px;margin-bottom:0px;padding-left:142px;}
 .avatar-menu h3{margin-top:0px;}
-.board-team{font-weight:bold;}.board-team a:link,.board-team a:active,.board-team a:visited,.board-team a:hover{color:#333333;font-size:130%;}
-.board-stat{font-size:180%;}.board-stat small{color:#999999;font-size:70%;}
 .well-post.rank-team{border:1px solid #0099e6;-webkit-box-shadow:0px 0px 0px 3px #66ccff;-moz-box-shadow:0px 0px 0px 3px #66ccff;box-shadow:0px 0px 0px 3px #66ccff;}
+.team-online.rank-team ul li{background-color:#0088cc;}.team-online.rank-team ul li div a{color:#ffffff;text-shadow:0px 1px 0px #000d13;}
+.team-online.rank-team ul li div .muted{color:#02435e;}
+.well-post.rank-mvp{border:1px solid #8753c0;-webkit-box-shadow:0px 0px 0px 3px #c8b0e2;-moz-box-shadow:0px 0px 0px 3px #c8b0e2;box-shadow:0px 0px 0px 3px #c8b0e2;}
+.team-online.rank-mvp ul li{background-color:#9466c6;}.team-online.rank-mvp ul li div a{color:#ffffff;text-shadow:0px 1px 0px #160c21;}
+.team-online.rank-mvp ul li div .muted{color:#3c2159;}
+.well-post.rank-active{border:1px solid #fa9f1e;-webkit-box-shadow:0px 0px 0px 3px #fdd49a;-moz-box-shadow:0px 0px 0px 3px #fdd49a;box-shadow:0px 0px 0px 3px #fdd49a;}
+.team-online.rank-active ul li{background-color:#f89406;}.team-online.rank-active ul li div a{color:#ffffff;text-shadow:0px 1px 0px #311d01;}
+.team-online.rank-active ul li div .muted{color:#7c4a03;}

+ 3 - 1
static/sora/css/sora.less

@@ -80,12 +80,14 @@
 @import "sora/avatars.less";
 @import "sora/navs.less";
 @import "sora/navbar.less";
+@import "sora/index.less";
 @import "sora/forums.less";
 @import "sora/threads.less";
 
 @import "sora/editor.less";
 @import "sora/markdown.less";
 @import "sora/utilities.less";
-@import "sora/ranks.less";
 
+// Keep ranks last for easy overrides!
+@import "ranks.less";
 @import "jquery.Jcrop.min.css";

+ 22 - 0
static/sora/css/sora/forums.less

@@ -13,6 +13,28 @@
         color: lighten(@textColor, 45%);
         font-size: 100%;
       }
+      
+      .form-inline {
+        float: right;
+        margin: 0px;
+        padding: 0px;
+        
+        .btn-link {
+          .opacity(25);
+          
+          &:hover, &:active {
+            background-color: lighten(@red, 20%);
+            .border-radius(3px);
+            .opacity(100);
+                      
+            color: @white;
+            
+            i {
+              background-image: url("@{iconWhiteSpritePath}");
+            }
+          }
+        } 
+      }
     }
   }
   

+ 121 - 0
static/sora/css/sora/index.less

@@ -0,0 +1,121 @@
+// Board index
+// -------------------------
+
+.list-empty {
+  margin-top: 32px;
+  
+  font-size: 180%;
+}
+
+.index-block {
+  .border-radius(3px);
+  padding: 8px;
+  margin-top: 8px;
+  margin-bottom: 24px;
+  
+  h3 {
+    border-bottom: 1px solid lighten(@grayLight, 15%);
+    
+    color: @grayLight;
+    font-size: 120%;
+    margin-top: -6px;
+    padding-top: 0px;
+  }
+  
+  ul {
+    margin: 0px;
+    margin-top: -10px;
+    padding: 0px;
+    
+    li {
+      border-bottom: 1px solid lighten(@grayLight, 25%);
+      margin: 0px;
+      overflow: auto;
+    }
+  }
+}
+
+.team-online {
+  margin-bottom: 0px;
+  padding-bottom: 0px;
+  
+  h3 {
+    border-bottom: none;
+    margin-bottom: 0px;
+    
+    color: darken(@grayLighter, 15%);
+  }
+  
+  ul {
+    margin: 0px -6px;
+    
+    li {
+      background-color: @grayLighter;
+      border: none;
+      .border-radius(3px);
+      margin-bottom: 8px;
+      padding: 6px;
+    } 
+  }
+   
+  div {
+    float: left;
+    position: relative;
+    top: 4px;
+    font-weight: bold;
+    
+    a {
+      display: block;
+      
+      color: @grayDark;
+      font-size: 160%;
+    }
+    
+    .muted {
+      color: @grayLight;
+    }
+  }
+  
+  img {
+    float: left;
+    margin-right: 12px;
+    width: 48px;
+    height: 48px;
+  }
+}
+
+.thread-ranking {
+  margin-bottom: 8px;
+  
+  li {
+    padding: 8px 0px;
+  }
+  
+  a {
+    color: @grayLight;
+    
+    &.lead {
+      display: block;
+      margin: 0px;
+      margin-bottom: -4px;
+      padding: 0px;
+      
+      color: @textColor;
+      font-size: 120%;
+      font-weight: bold;
+    }
+  }
+}
+
+.forum-stats {  
+  color: lighten(@grayLight, 10%);
+  
+  strong {
+    padding-left: 8px;
+    
+    color: @gray;
+    font-size: 170%;
+  }
+}
+
+.cookie-message {}

+ 0 - 12
static/sora/css/sora/ranks.less

@@ -1,12 +0,0 @@
-// Ranks styles
-// -------------------------
-
-// .rank-team
-.well-post.rank-team {
-  border: 1px solid lighten(@linkColor, 5%);
-  .box-shadow(0px 0px 0px 3px lighten(@linkColor, 30%));
-}
-
-// .rank-mvp
-
-// .rank-active

+ 2 - 1
static/sora/css/sora/scaffolding.less

@@ -8,6 +8,7 @@
   }
   
   &.bottom {
+    background-color: darken(@bodyBackground, 10%);
     margin-top: 24px;
     margin-bottom: 0px;
   }
@@ -26,7 +27,7 @@ footer {
   padding-top: 12px;
   padding-bottom: 32px;
   
-  color: darken(@bodyBackground, 30%);
+  color: darken(@bodyBackground, 35%);
   
   a, a:link, a:active, a:visited {
     color: darken(@bodyBackground, 30%);

+ 1 - 19
static/sora/css/sora/utilities.less

@@ -43,22 +43,4 @@
   h3 {
     margin-top: 0px;
   }
-}
-
-.board-team {
-  font-weight: bold;
-    
-  a:link, a:active, a:visited, a:hover {
-    color: @textColor;
-    font-size: 130%;
-  }
-}
-
-.board-stat {
-  font-size: 180%;
-  
-  small {
-    color: @grayLight;
-    font-size: 70%;
-  }
-}
+}

+ 1 - 1
templates/admin/ranks/list.html

@@ -11,7 +11,7 @@
 
 {% block table_row scoped %}
   <td class="lead-cell">
-  	<strong>{{ item.name }}</strong>{% if item.special %} <span class="label label-info">{% trans %}Special{% endtrans %}</span>{% endif %}{% if item.as_tab %} <span class="label label-inverse">{% trans %}Tab{% endtrans %}</span>{% endif %}
+  	<strong>{{ item.name }}</strong>{% if item.special %} <span class="label label-info">{% trans %}Special{% endtrans %}</span>{% endif %}{% if item.as_tab %} <span class="label label-inverse">{% trans %}Tab{% endtrans %}</span>{% endif %}{% if item.on_index %} <span class="label label-orange">{% trans %}On Index{% endtrans %}</span>{% endif %}
   </td>
   <td class="span2">
   	{{ form_theme.field_widget(table_form['pos_' + item.pk|string], attrs={'form': 'table_form'}, width=2) }}

+ 48 - 19
templates/sora/index.html

@@ -15,34 +15,63 @@
     <div class="forums-list">
       {% for category in forums_list %}{% if category.subforums %}
       <div class="category{% if category.style %} {{ category.style }}{% endif %}">
-        <h2>{{ category.name }}{% if category.description %} <small><strong>{{ category.description }}</strong></small>{% endif %}</h2>
+        <h2>{{ category.name }}{% if category.description %} <small><strong>{{ category.description }}</strong></small>{% endif %}
+        {%- if user.is_authenticated() -%}
+        <form action="{% url 'read_all' %}" method="post" class="form-inline">
+          <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+          <button type"submit" class="btn btn-link"><i class="icon-ok"></i> {% trans %}Mark forums read{% endtrans %}</button>
+        </form>
+        {%- endif %}</h2>
         {{ macros.draw_forums(category, 8) }}
-      </div>{% endif %}{% endfor %}
+      </div>{% endif %}
+      {% else %}
+      <p class="list-empty">{% trans %}Looks like there are no forums that you have permission to see.{% endtrans %}</p>
+      {% endfor %}
     </div>
   </div>
   <div class="span4 forum-list-side">
-    {% if team_online %}
-    <h3>{% trans %}Team Online{% endtrans %}</h3>
-    {% for user in team_online %}
-    <div class="board-team">
-      <img src="{{ user.get_avatar(28) }}" alt="" class="avatar-small"> <a href="{% url 'user' username=user.username_slug, user=user.pk %}">{{ user.username }}</a>{% if user.get_title() %} <span class="muted">{{ _(user.get_title()) }}</span>{% endif %}
+    
+    {% for rank in ranks_online %}{% if rank.online %}
+    <div class="index-block team-online{% if rank.style %} {{ rank.style }}{% endif %}">
+      <h3>{% trans rank_name=_(rank.name) %}{{ rank_name }} Online{% endtrans %}</h3>
+      <ul class="unstyled">
+        {% for user in rank.online %}
+        <li>
+          <img src="{{ user.get_avatar(48) }}" alt="" class="avatar-small">
+          <div>
+            <a href="{% url 'user' username=user.username_slug, user=user.pk %}">{{ user.username }}</a>
+            {% if rank.title or user.title %}<span class="muted">{% if user.title %}{{ user.title }}{% else %}{{ _(rank.title) }}{% endif %}</span>{% endif %}
+          </div>
+        </li>
+        {% endfor %}
+      </ul>
     </div>
-    <hr>
-    {% endfor %}
-    {% endif %}
+    {% endif %}{% endfor %}
     
     {% if popular_threads %}
-    <h3>{% trans %}Popular Threads{% endtrans %}</h3>
-    {% for thread in popular_threads %}
-    <a href="{% url 'thread' thread=thread.pk, slug=thread.slug %}" class="lead">{{ thread.name }}</a><br />
-    <a href="{% url 'forum' forum=thread.forum.pk, slug=thread.forum.slug %}">{{ thread.forum.name }}</a> - {{ thread.last|reltimesince }}
-    <hr>
-    {% endfor %}
+    <div class="index-block thread-ranking">
+      <h3>{% trans %}Popular Threads{% endtrans %}</h3>
+      <ul class="unstyled">
+        {% for thread in popular_threads %}
+        <li>
+          <a href="{% url 'thread' thread=thread.pk, slug=thread.slug %}" class="lead">{{ thread.name }}</a>
+          <div class="muted"><a href="{% url 'forum' forum=thread.forum_id, slug=thread.forum_slug %}">{{ thread.forum_name }}</a> - {{ thread.last|reltimesince }}</div>
+        </li>
+        {% endfor %}
+      </ul>
+    </div>
     {% endif %}
     
-    <h3>{% trans %}Forum Stats{% endtrans %}</h3>
-    <p class="lead board-stat">{{ monitor.posts|int|intcomma }} <small>{% trans %}Posts{% endtrans %}</small></p>
-    <p class="lead board-stat">{{ monitor.users|int|intcomma }} <small>{% trans %}Members{% endtrans %}</small></p>
+    <div class="row forum-stats">
+      <div class="span2">
+        <strong>{{ monitor.posts|int|intcomma }}</strong>
+        {% trans %}Posts{% endtrans %}
+      </div>
+      <div class="span2">
+        <strong>{{ monitor.users|int|intcomma }}</strong>
+        {% trans %}Members{% endtrans %}
+      </div>
+    </div>
   </div>
 </div>
 {% endblock %}