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

Beginnings of threads list abstraction, threads and announcements types.

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

+ 1 - 1
misago/apps/announcements/urls.py

@@ -2,5 +2,5 @@ from django.conf.urls import patterns, url
 
 urlpatterns = patterns('misago.apps.announcements.views',
     url(r'^$', 'ThreadsListView', name="announcements"),
-    url(r'^(?P<slug>(\w|-)+)-(?P<forum>\d+)/(?P<page>\d+)/$', 'ThreadsView', name="announcements"),
+    url(r'^(?P<page>\d+)/$', 'ThreadsListView', name="announcements"),
 )

+ 22 - 0
misago/apps/announcements/views.py

@@ -0,0 +1,22 @@
+from misago.apps.forumbase.list import ThreadsListBaseView
+from misago.models import Forum, Thread
+from misago.readstrackers import ThreadsTracker
+from misago.utils.pagination import make_pagination
+from misago.apps.announcements.mixins import TypeMixin
+
+class ThreadsListView(ThreadsListBaseView, TypeMixin):
+    def fetch_forum(self):
+        self.forum = Forum.objects.get(special='announcements')
+
+    def fetch_threads(self):
+        queryset = self.request.acl.threads.filter_threads(self.request, self.forum, Thread.objects.filter(forum=self.forum))
+        self.count = queryset.count()
+        self.pagination = make_pagination(self.kwargs.get('page', 0), self.count, self.request.settings.threads_per_page)
+        
+        if self.request.settings.avatars_on_threads_list:
+            queryset = queryset.prefetch_related('start_poster', 'last_poster')
+
+        tracker = ThreadsTracker(self.request, self.forum)
+        for thread in queryset[self.pagination['start']:self.pagination['stop']]:
+            thread.is_read = tracker.is_read(thread)
+            self.threads.append(thread)

+ 6 - 4
misago/apps/forumbase/list.py

@@ -3,7 +3,7 @@ from misago.acl.exceptions import ACLError403, ACLError404
 from misago.apps.errors import error403, error404
 from misago.forms import FormFields
 from misago.models import Forum
-from misago.readstrackers import ForumsTracker, ThreadsTracker
+from misago.readstrackers import ForumsTracker
 
 class ThreadsListBaseView(object):
     def _fetch_forum(self):
@@ -14,7 +14,6 @@ class ThreadsListBaseView(object):
             self.parents = Forum.objects.forum_parents(self.forum.pk)
         if self.forum.lft + 1 != self.forum.rght:
             self.forum.subforums = Forum.objects.treelist(self.request.acl.forums, self.forum, tracker=ForumsTracker(self.request.user))
-        self.tracker = ThreadsTracker(self.request, self.forum)
     
     def __new__(cls, request, **kwargs):
         obj = super(ThreadsListBaseView, cls).__new__(cls)
@@ -25,6 +24,7 @@ class ThreadsListBaseView(object):
         self.kwargs = kwargs
         self.pagination = {}
         self.parents = []
+        self.threads = []
         self.message = request.messages.get_message('threads')
         try:
             self._fetch_forum()
@@ -38,11 +38,13 @@ class ThreadsListBaseView(object):
         except Forum.DoesNotExist:
             return error404(request)
         except ACLError403 as e:
-            return error403(request, e.message)
+            return error403(request, unicode(e))
         except ACLError404 as e:
-            return error404(request, e.message)
+            return error404(request, unicode(e))
+
         # Merge proxy into forum
         self.forum.closed = self.proxy.closed
+        
         return request.theme.render_to_response(('%s/list.html' % self.templates_prefix),
                                                 {
                                                  'message': self.message,

+ 22 - 32
misago/apps/threads/views.py

@@ -1,5 +1,7 @@
+from itertools import chain
 from misago.apps.forumbase.list import ThreadsListBaseView
 from misago.models import Forum, Thread
+from misago.readstrackers import ThreadsTracker
 from misago.utils.pagination import make_pagination
 from misago.apps.threads.mixins import TypeMixin
 
@@ -9,9 +11,9 @@ class ThreadsListView(ThreadsListBaseView, TypeMixin):
 
     def threads_queryset(self):
         announcements = Forum.objects.special_model('announcements')
-        annos_global = self.request.acl.threads.filter_threads(self.request, announcements, announcements.thread_set).order_by('-weight')
+        annos_global = announcements.thread_set.filter(deleted=False).filter(moderated=False)
         annos_forum = self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set).filter(weight=2)
-        rest = self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set).filter(weight=2)
+        rest = self.request.acl.threads.filter_threads(self.request, self.forum, self.forum.thread_set).filter(weight__lt=2)
 
         # Dont display threads by ignored users (unless they are important)
         if self.request.user.is_authenticated():
@@ -19,38 +21,26 @@ class ThreadsListView(ThreadsListBaseView, TypeMixin):
             if ignored_users:
                 rest = rest.extra(where=["`threads_thread`.`start_poster_id` IS NULL OR `threads_thread`.`start_poster_id` NOT IN (%s)" % ','.join([str(i) for i in ignored_users])])
 
-        # Return two 
+        # Add in first and last poster
         if self.request.settings.avatars_on_threads_list:
-            return ((annos_global | annos_forum | rest).prefetch_related('start_poster', 'last_poster'),
-                    rest.prefetch_related('start_poster', 'last_poster'))
-        return (annos_global | annos_forum | rest), rest
+            annos_global = annos_global.prefetch_related('start_poster', 'last_poster')
+            annos_forum = annos_forum.prefetch_related('start_poster', 'last_poster')
+            rest = rest.prefetch_related('start_poster', 'last_poster')
+
+        return annos_global, annos_forum, rest
 
     def fetch_threads(self):
-        self.threads = []
-        ignored_users = []
-        queryset, threads = self.threads_queryset()
-        for thread in queryset:
-            self.threads.append(thread)
-        self.count =threads.count()
+        qs_global, qs_forum, qs_rest = self.threads_queryset()
+        self.count = qs_rest.count()
         self.pagination = make_pagination(self.kwargs.get('page', 0), self.count, self.request.settings.threads_per_page)
-        """
-        queryset_anno = Thread.objects.filter(Q(forum=Forum.objects.token_to_pk('announcements')) | (Q(forum=self.forum) & Q(weight=2)))
-        queryset_threads = self.request.acl.threads.filter_threads(self.request, self.forum, Thread.objects.filter(forum=self.forum).filter(weight__lt=2)).order_by('-weight', '-last')
-        if self.request.user.is_authenticated():
-            ignored_users = self.request.user.ignored_users()
-            if ignored_users:
-                queryset_threads = queryset_threads.extra(where=["`threads_thread`.`start_poster_id` IS NULL OR `threads_thread`.`start_poster_id` NOT IN (%s)" % ','.join([str(i) for i in ignored_users])])
-        if self.request.settings.avatars_on_threads_list:
-            queryset_anno = queryset_anno.prefetch_related('start_poster', 'last_post')
-            queryset_threads = queryset_threads.prefetch_related('start_poster', 'last_poster')
-        for thread in queryset_anno:
-            self.threads.append(thread)
-        for thread in queryset_threads:
-            self.threads.append(thread)
-        if self.request.settings.threads_per_page < self.count:
-            self.threads = self.threads[self.pagination['start']:self.pagination['stop']]
-        for thread in self.threads:
+
+        tracker_annos = ThreadsTracker(self.request, Forum.objects.special_model('announcements'))
+        tracker_forum = ThreadsTracker(self.request, self.forum)
+
+        for thread in list(chain(qs_global, qs_forum, qs_rest[self.pagination['start']:self.pagination['stop']])):
             if thread.forum_id == self.forum.pk:
-                thread.is_read = self.tracker.is_read(thread)
-            thread.last_poster_ignored = thread.last_poster_id in ignored_users
-        """
+                thread.is_read = tracker_forum.is_read(thread)
+            else:
+                thread.weight = 2
+                thread.is_read = tracker_annos.is_read(thread)
+            self.threads.append(thread)

+ 4 - 0
misago/context_processors.py

@@ -1,6 +1,7 @@
 from django.conf import settings
 from misago import __version__
 from misago.admin import site
+from misago.models import Forum
 
 def common(request):
     try:
@@ -18,6 +19,9 @@ def common(request):
             'stopwatch': request.stopwatch.time(),
             'user': request.user,
             'version': __version__,
+            'announcements': Forum.objects.special_model('announcements'),
+            'private': Forum.objects.special_model('private'),
+            'reports': Forum.objects.special_model('reports'),
         }
     except AttributeError:
         # If request lacks required service, let template crash in context processor's stead

+ 3 - 3
misago/fixtures/userroles.py

@@ -15,7 +15,7 @@ def load():
                         'can_see_users_emails': True,
                         'can_see_users_trails': True,
                         'can_see_hidden_users': True,
-                        'forums': {5: 1, 6: 1, 7: 1},
+                        'forums': {1: 1, 2: 1, 3: 1, 5: 1, 6: 1, 7: 1},
                        }
     role.save(force_insert=True)
     
@@ -30,7 +30,7 @@ def load():
                         'can_see_users_emails': True,
                         'can_see_users_trails': True,
                         'can_see_hidden_users': True,
-                        'forums': {5: 1, 6: 1, 7: 1},
+                        'forums': {1: 1, 2: 1, 3: 1, 5: 1, 6: 1, 7: 1},
                        }
     role.save(force_insert=True)
     
@@ -39,7 +39,7 @@ def load():
                         'name_changes_allowed': 2,
                         'can_use_signature': False,
                         'can_search_users': True,
-                        'forums': {5: 3, 6: 3, 7: 3},
+                        'forums': {2: 3, 5: 3, 6: 3, 7: 3},
                        }
     role.save(force_insert=True)
     

+ 1 - 1
misago/settings_base.py

@@ -87,8 +87,8 @@ JINJA2_EXTENSIONS = (
 # List of application middlewares
 MIDDLEWARE_CLASSES = (
     'misago.middleware.stopwatch.StopwatchMiddleware',
-    'debug_toolbar.middleware.DebugToolbarMiddleware',
     'misago.middleware.heartbeat.HeartbeatMiddleware',
+    'debug_toolbar.middleware.DebugToolbarMiddleware',
     'misago.middleware.cookiejar.CookieJarMiddleware',
     'misago.middleware.settings.SettingsMiddleware',
     'misago.middleware.monitor.MonitorMiddleware',

+ 2 - 2
misago/urls.py

@@ -27,10 +27,10 @@ urlpatterns += patterns('',
     (r'^activate/', include('misago.apps.activation.urls')),
     (r'^watched-threads/', include('misago.apps.watchedthreads.urls')),
     (r'^reset-password/', include('misago.apps.resetpswd.urls')),
-    (r'^', include('misago.apps.threads.urls')),
+    (r'^announcements/', include('misago.apps.announcements.urls')),
     #(r'^private-discussions/', include('misago.apps.privatethreads.urls')),
-    #(r'^announcements/', include('misago.apps.announcements.urls')),
     #(r'^reports/', include('misago.apps.reports.urls')),
+    (r'^', include('misago.apps.threads.urls')),
 )
 
 

+ 209 - 0
templates/cranefly/announcements/list.html

@@ -0,0 +1,209 @@
+{% extends "cranefly/layout.html" %}
+{% import "_forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=_('Global Announcements'),page=pagination['page']) }}{% endblock %}
+
+{% block container %}
+<div class="page-header header-primary">
+  <div class="container">
+    {{ messages_list(messages) }}
+    <h1>{% trans %}Global Announcements{% endtrans %}</h1>
+  </div>
+</div>
+
+<div class="container container-primary">
+
+  <div class="lead page-description">
+    <p>{% trans%}Global announcements are visible of top of all forums threads lists.{% endtrans %}</p>
+  </div>
+
+  {% if message %}
+  <div class="messages-list">
+    {{ macros.draw_message(message) }}
+  </div>
+  {% endif %}
+
+  <div class="forum-threads-extra extra-top">
+    {{ pager() }}
+    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
+    <a href="{% url 'thread_new' forum=forum.pk, slug=forum.slug %}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-list">
+    <table class="table">
+      <thead>
+        <tr>
+          <th>{% trans %}Thread{% endtrans %}</th>
+          <th class="span1">{% trans %}Rating{% endtrans %}</th>
+          <th class="span5">{% trans %}Activity{% endtrans %}</th>
+          {% if user.is_authenticated() and list_form %}
+          <th class="check-cell"><label class="checkbox"><input type="checkbox" class="checkbox-master"></label></th>
+          {% endif %}
+        </tr>
+      </thead>
+      <tbody>
+        {% for thread in threads %}
+        <tr>
+          <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 -%}
+            {% 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>
+            <span class="thread-details">
+              {% trans user=thread_starter(thread), start=thread.start|reldate|low %}by {{ user }} {{ start }}{% endtrans %}
+            </span>
+            <ul class="unstyled thread-flags">
+              {% if thread.replies_reported %}
+              <li><i class="icon-warning-sign tooltip-top" title="{% trans %}This thread has reported replies{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.replies_moderated %}
+              <li><i class="icon-question-sign tooltip-top" title="{% trans %}This thread has unreviewed replies{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.weight == 2 %}
+              <li><i class="icon-star tooltip-top" title="{% trans %}This thread is an annoucement{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.weight == 1 %}
+              <li><i class="icon-star-empty tooltip-top" title="{% trans %}This thread is sticky{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.moderated  %}
+              <li><i class="icon-eye-close tooltip-top" title="{% trans %}This thread awaits review{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.deleted %}
+              <li><i class="icon-trash tooltip-top" title="{% trans %}This thread is deleted{% endtrans %}"></i></li>
+              {% endif %}
+              {% if thread.closed %}
+              <li><i class="icon-lock tooltip-top" title="{% trans %}This thread is closed{% endtrans %}"></i></li>
+              {% endif %}
+            </ul>
+          </td>
+          <td>
+            <div class="thread-rating{% if (thread.upvotes-thread.downvotes) > 0 %} thread-rating-positive{% elif (thread.upvotes-thread.downvotes) < 0 %} thread-rating-negative{% endif %}">
+              {% if (thread.upvotes-thread.downvotes) > 0 %}+{% elif (thread.upvotes-thread.downvotes) < 0 %}-{% endif %}{{ (thread.upvotes-thread.downvotes)|abs|intcomma }}
+            </div>
+          </td>
+          <td>
+            <div class="thread-last-reply">
+              {{ replies(thread.replies) }} - {% trans user=thread_reply(thread), last=thread.last|reldate|low %}last by {{ user }} {{ last }}{% endtrans %}
+            </div>
+          </td>
+          {% if user.is_authenticated() and list_form %}
+          <td class="check-cell">{% if thread.forum_id == forum.pk %}<label class="checkbox"><input form="threads_form" name="{{ list_form['list_items']['html_name'] }}" type="checkbox" class="checkbox-member" value="{{ thread.pk }}"{% if list_form['list_items']['has_value'] and ('' ~ thread.pk) in list_form['list_items']['value'] %} checked="checked"{% endif %}></label>{% else %}&nbsp;{% endif %}</td>
+          {% endif %}
+        </tr>
+        {% else %}
+        <tr>
+          <td colspan="4" class="threads-list-empty">
+            {% trans %}There are no threads in this forum.{% endtrans %}
+          </td>
+        </tr>
+        {% endfor %}
+      </tbody>
+    </table>
+    {% if user.is_authenticated() and list_form %}
+    <div class="threads-actions">
+      <form id="threads_form" class="form-inline pull-right" action="{% url 'forum' slug=forum.slug, forum=forum.id, page=pagination['page'] %}" method="POST">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        {{ form_theme.input_select(list_form['list_action'],width=3) }}
+        <button type="submit" class="btn btn-danger">{% trans %}Go{% endtrans %}</button>
+      </form>
+    </div>
+    {% endif %}
+  </div>
+
+  <div class="forum-threads-extra">
+    {{ pager() }}
+    {% if user.is_authenticated() and acl.threads.can_start_threads(forum) %}
+    <a href="{% url 'thread_new' forum=forum.pk, slug=forum.slug %}" class="btn btn-inverse pull-right"><i class="icon-plus"></i> {% trans %}New Thread{% endtrans %}</a>
+    {% elif not user.is_authenticated() and not user.is_crawler() %}
+    <p class="lead threads-signin-message"><a href="{% url 'sign_in' %}">{% trans %}Sign in or register to start threads.{% endtrans %}</a></p>
+    {% endif %}
+  </div>
+
+</div>
+{% endblock %}
+
+
+{% macro forum_stats(forum) -%}
+{% if forum.last_thread_id and not forum.attr('hidethread') -%}
+  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta), thread=forum_thread(forum) -%}
+  {{ posts }} post - last in {{ thread }}
+  {%- pluralize -%}
+  {{ posts }} posts - last in {{ thread }}
+  {%- endtrans %}
+{%- else -%}
+  {% trans count=forum.posts, posts=fancy_number(forum.posts, forum.posts_delta) -%}
+  {{ posts }} post
+  {%- pluralize -%}
+  {{ posts }} posts
+  {%- endtrans %}
+{%- endif %}
+{%- endmacro %}
+
+{% macro forum_thread(forum) -%}
+<a href="{% url 'thread_new' thread=forum.last_thread_id, slug=forum.last_thread_slug %}">{{ forum.last_thread_name }}</a>
+{%- endmacro %}
+
+{% macro redirect_stats(forum) -%}
+{% trans count=forum.redirects, redirects=fancy_number(forum.redirects, forum.redirects_delta) -%}
+{{ redirects }} click
+{%- pluralize -%}
+{{ redirects }} clicks
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro fancy_number(number, delta) -%}
+<strong{% if delta < number %} class="stat-increment"{% endif %}>{{ number|intcomma }}</strong>
+{%- endmacro %}
+
+{% macro replies(thread_replies) -%}
+{% trans count=thread_replies, replies=('<strong>' ~ (thread_replies|intcomma) ~ '</strong>')|safe -%}
+{{ replies }} reply
+{%- pluralize -%}
+{{ replies }} replies
+{%- endtrans %}
+{%- endmacro %}
+
+{% macro thread_starter(thread) -%}
+{% 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_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 %}
+{%- endmacro %}
+
+{% macro pager() %}
+{% if pagination['total'] > 0 %}
+<div class="pagination pull-left">
+  <ul>
+    <li class="count">{{ macros.pager_label(pagination) }}</li>
+    {%- if pagination['prev'] > 1 %}<li><a href="{% url 'forum' slug=forum.slug, forum=forum.id %}" class="tooltip-top" title="{% trans %}First Page{% endtrans %}"><i class="icon-chevron-left"></i> {% trans %}First{% endtrans %}</a></li>{% endif -%}
+    {%- if pagination['prev'] > 0 %}<li><a href="{%- if pagination['prev'] > 1 %}{% url 'forum' slug=forum.slug, forum=forum.id, page=pagination['prev'] %}{% else %}{% url 'forum' slug=forum.slug, forum=forum.id %}{% endif %}" class="tooltip-top" title="{% trans %}Newest Threads{% endtrans %}"><i class="icon-chevron-left"></i></a></li>{% endif -%}
+    {%- if pagination['next'] > 0 %}<li><a href="{% url 'forum' slug=forum.slug, forum=forum.id, page=pagination['next'] %}" class="tooltip-top" title="{% trans %}Older Threads{% endtrans %}"><i class="icon-chevron-right"></i></a></li>{% endif -%}
+  </ul>
+</div>
+{% endif %}
+{% endmacro %}
+
+{% block javascripts -%}
+{{ super() }}
+{%- if user.is_authenticated() and list_form %}
+  <script type="text/javascript">
+    $(function () {
+      $('#threads_form').submit(function() {
+        if ($('.check-cell[]:checked').length == 0) {
+          alert("{% trans %}You have to select at least one thread.{% endtrans %}");
+          return false;
+        }
+        if ($('#id_list_action').val() == 'hard') {
+          var decision = confirm("{% trans %}Are you sure you want to delete selected threads? This action is not reversible!{% endtrans %}");
+          return decision;
+        }
+        return true;
+      });
+    });
+  </script>{% endif %}
+{%- endblock %}

+ 4 - 2
templates/cranefly/layout.html

@@ -10,9 +10,11 @@
       <div class="container">
         <ul class="nav">
           <li><a href="#">Frontlines</a></li>
-          <li class="active"><a href="#">Reports <span class="label label-important">5</span></a></li>
+          <li><a href="#">Reports <span class="label label-important">5</span></a></li>
           <li><a href="#">Warnings</a></li>
-          <li><a href="#">Annoucements</a></li>
+          {% if acl.forums.can_browse(announcements) %}
+          <li{% if forum and forum.special == 'announcements' %} class="active"{% endif %}><a href="{% url 'announcements' %}">{% trans %}Manage Announcements{% endtrans %}</a></li>
+          {% endif %}
         </ul>
         <form class="navbar-form pull-right">
           <div class="navbar-search-form">

+ 3 - 3
templates/debug_toolbar/base.html

@@ -1,10 +1,10 @@
 {% load i18n %}
 <style type="text/css">
 @media print { #djDebug {display:none;}}
-{{ css }}
 </style>
-<script type="text/javascript">{{ js }}</script>
-<div id="djDebug" style="display:none;">
+<link rel="stylesheet" href="{{ STATIC_URL }}debug_toolbar/css/toolbar.min.css" type="text/css">
+<script type="text/javascript" src="{{ STATIC_URL }}debug_toolbar/js/toolbar.min.js"></script>
+<div id="djDebug" style="display:none;" dir="ltr">
 	<div style="display:none;" id="djDebugToolbar">
 		<ul id="djDebugPanelList">
 			{% if panels %}

+ 51 - 38
templates/debug_toolbar/panels/cache.html

@@ -1,56 +1,69 @@
 {% load i18n %}
+<h3>{% trans "Summary" %}</h3>
 <table>
-	<colgroup>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-		<col width="12%"/>
-	</colgroup>
+	<thead>
 	<tr>
 		<th>{% trans "Total Calls" %}</th>
-		<td>{{ cache_calls }}</td>
 		<th>{% trans "Total Time" %}</th>
-		<td>{{ cache_time }}ms</td>
-		<th>{% trans "Hits" %}</th>
-		<td>{{ cache.hits }}</td>
-		<th>{% trans "Misses" %}</th>
-		<td>{{ cache.misses }}</td>
+		<th>{% trans "Cache Hits" %}</th>
+		<th>{% trans "Cache Misses" %}</th>
+	</tr>
+	</thead>
+	<tbody>
+	<tr>
+		<td>{{ total_calls }}</td>
+		<td>{{ total_time }} ms</td>
+		<td>{{ hits }}</td>
+		<td>{{ misses }}</td>
+	</tr>
+	</tbody>
+</table>
+<h3>{% trans "Commands" %}</h3>
+<table>
+	<thead>
+	<tr>
+	{% for name in counts.iterkeys %}
+		<th>{{ name }}</th>
+	{% endfor %}
 	</tr>
+	</thead>
+	<tbody>
 	<tr>
-		<th>gets</th>
-		<td>{{ cache.gets }}</td>
-		<th>sets</th>
-		<td>{{ cache.sets }}</td>
-		<th>deletes</th>
-		<td>{{ cache.deletes }}</td>
-		<th>get_many</th>
-		<td>{{ cache.get_many }}</td>
+	{% for value in counts.itervalues %}
+		<td>{{ value }}</td>
+	{% endfor %}
 	</tr>
+	</tbody>
 </table>
-{% if cache.calls %}
-<h3>{% trans "Breakdown" %}</h3>
+{% if calls %}
+<h3>{% trans "Calls" %}</h3>
 <table>
 	<thead>
 		<tr>
-			<th>{% trans "Time" %}&nbsp;(ms)</th>
+			<th colspan="2">{% trans "Time (ms)" %}</th>
 			<th>{% trans "Type" %}</th>
-			<th>{% trans "Parameters" %}</th>
-			<th>{% trans "Function" %}</th>
+			<th>{% trans "args" %}</th>
+			<th>{% trans "kwargs" %}</th>
+			<th>{% trans "Backend" %}</th>
 		</tr>
 	</thead>
 	<tbody>
-		{% for query in cache.calls %}
-			<tr class="{% cycle 'row1' 'row2' %}">
-				<td>{{ query.0|floatformat:"4" }}</td>
-				<td>{{ query.1|escape }}</td>
-				<td>{{ query.2|escape }}</td>
-				<td><acronym title="{{ query.3.0 }}:{{ query.3.1 }}">{{ query.3.2|escape }}</acronym>: {{ query.3.3.0|escape }}</td>
-			</tr>
-		{% endfor %}
+	{% for call in calls %}
+		<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}" id="cacheMain_{{ forloop.counter }}">
+			<td class="toggle">
+				<a class="djToggleSwitch" data-toggle-name="cacheMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a>
+			</td>
+			<td>{{ call.time|floatformat:"4" }}</td>
+			<td>{{ call.name|escape }}</td>
+			<td>{{ call.args|escape }}</td>
+			<td>{{ call.kwargs|escape }}</td>
+			<td>{{ call.backend }}</td>
+		</tr>
+		<tr class="djUnselected djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %} djToggleDetails_{{ forloop.counter }}" id="cacheDetails_{{ forloop.counter }}">
+			<td colspan="1"></td>
+			<td colspan="5"><pre class="stack">{{ call.trace }}</pre></td>
+		</tr>
+	{% endfor %}
 	</tbody>
 </table>
-{% endif %}
+{% endif %}

+ 1 - 1
templates/debug_toolbar/panels/logger.html

@@ -16,7 +16,7 @@
 					<td>{{ record.level }}</td>
 					<td>{{ record.time|date:"h:i:s m/d/Y" }}</td>
 					<td>{{ record.channel|default:"-" }}</td>
-					<td>{{ record.message }}</td>
+					<td>{{ record.message|linebreaksbr }}</td>
 					<td>{{ record.file }}:{{ record.line }}</td>
 				</tr>
 			{% endfor %}

+ 13 - 6
templates/debug_toolbar/panels/profiling.html

@@ -4,17 +4,17 @@
 	<thead>
 		<tr>
 			<th>{% trans "Call" %}</th>
-			<th>{% trans "TotTime" %}</th>
-			<th>{% trans "Per" %}</th>
 			<th>{% trans "CumTime" %}</th>
 			<th>{% trans "Per" %}</th>
+			<th>{% trans "TotTime" %}</th>
+			<th>{% trans "Per" %}</th>
 			<th>{% trans "Count" %}</th>
 		</tr>
 	</thead>
 	<tbody>
 		{% for call in func_list %}
 			<!--  style="background:{{ call.background }}" -->
-			<tr id="{{ call.id }}" class="djDebugProfileRow{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}" depth="{{ call.depth }}">
+			<tr class="djDebugProfileRow{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}" depth="{{ call.depth }}">
 				<td>
 					<div style="padding-left: {{ call.indent }}px;">
 						{% if call.has_subfuncs %}
@@ -25,12 +25,19 @@
 						<span class="stack">{{ call.func_std_string }}</span>
 					</div>
 				</td>
-				<td>{{ call.tottime|floatformat:3 }}</td>
-				<td>{{ call.tottime_per_call|floatformat:3 }}</td>
 				<td>{{ call.cumtime|floatformat:3 }}</td>
 				<td>{{ call.cumtime_per_call|floatformat:3 }}</td>
+				<td>{{ call.tottime|floatformat:3 }}</td>
+				<td>{{ call.tottime_per_call|floatformat:3 }}</td>
 				<td>{{ call.count }}</td>
 			</tr>
+			{% if call.line_stats_text %}
+				<tr class="djToggleDetails_{{ call.id }}{% for parent_id in call.parent_ids %} djToggleDetails_{{ parent_id }}{% endfor %}">
+					<td colspan="6">
+						<div style="padding-left: {{ call.indent }}px;"><pre>{{ call.line_stats_text }}</pre></div>
+					</td>
+				</tr>
+			{% endif %}
 		{% endfor %}
 	</tbody>
-</table>
+</table>

+ 2 - 0
templates/debug_toolbar/panels/request_vars.html

@@ -5,6 +5,7 @@
 	<thead>
 		<tr>
 			<th>{% trans 'View Function' %}</th>
+			<th>{% trans 'URL Name' %}</th>
 			<th>{% trans 'args' %}</th>
 			<th>{% trans 'kwargs' %}</th>
 		</tr>
@@ -12,6 +13,7 @@
 	<tbody>
 		<tr>
 			<td>{{ view_func }}</td>
+			<td>{{ view_urlname }}</td>
 			<td>{{ view_args|default:"None" }}</td>
 			<td>
 			{% if view_kwargs.items %}

+ 3 - 3
templates/debug_toolbar/panels/settings_vars.html

@@ -7,10 +7,10 @@
 		</tr>
 	</thead>
 	<tbody>
-		{% for var in settings.items|dictsort:"0" %}
+		{% for name, value in settings.items %}
 			<tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}">
-				<td>{{ var.0 }}</td>
-				<td><code>{{ var.1|pprint }}</code></td>
+				<td>{{ name }}</td>
+				<td><code>{{ value|pprint }}</code></td>
 			</tr>
 		{% endfor %}
 	</tbody>

+ 1 - 1
templates/debug_toolbar/panels/signals.html

@@ -2,7 +2,7 @@
 <table>
 	<thead>
 		<tr>
-			<th>{% trans "Signal" %}</th>
+			<th>{% trans 'Signal' %}</th>
 			<th>{% trans 'Providing Args' %}</th>
 			<th>{% trans 'Receivers' %}</th>
 		</tr>

+ 11 - 10
templates/debug_toolbar/panels/sql.html

@@ -1,4 +1,5 @@
 {% load i18n %}
+{% load debug_toolbar_utils %}
 <div class="clearfix">
 	<ul class="stats">
 		{% for alias, info in databases %}
@@ -26,7 +27,7 @@
 				<tr class="djDebugHoverable {% cycle 'djDebugOdd' 'djDebugEven' %}{% if query.is_slow %} djDebugRowWarning{% endif %}{% if query.starts_trans %} djDebugStartTransaction{% endif %}{% if query.ends_trans %} djDebugEndTransaction{% endif %}{% if query.in_trans %} djDebugInTransaction{% endif %}" id="sqlMain_{{ forloop.counter }}">
 					<td class="color"><span style="background-color: rgb({{ query.rgb_color|join:", " }});">&nbsp;</span></td>
 					<td class="toggle">
-						<a class="djToggleSwitch" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a>
+						<a class="djToggleSwitch" data-toggle-name="sqlMain" data-toggle-id="{{ forloop.counter }}" data-toggle-open="+" data-toggle-close="-" href="javascript:void(0)">+</a>
 					</td>
 					<td class="query">
 						<div class="djDebugSqlWrap">
@@ -34,7 +35,7 @@
 						</div>
 					</td>
 					<td class="timeline">
-						<div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="left:{{ query.start_offset }}%;"><strong style="width:{{ query.width_ratio }}%;">{{ query.width_ratio }}%</strong></div></div>
+						<div class="djDebugTimeline"><div class="djDebugLineChart{% if query.is_slow %} djDebugLineChartWarning{% endif %}" style="left:{{ query.start_offset|dotted_number }}%;"><strong style="width:{{ query.width_ratio_relative|dotted_number }}%;">{{ query.width_ratio }}%</strong></div></div>
 					</td>
 					<td class="time">
 						{{ query.duration|floatformat:"2" }}
@@ -42,10 +43,10 @@
 					<td class="actions">
 					{% if query.params %}
 						{% if query.is_select %}
-							<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Sel</a>
-							<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Expl</a>
+							<a class="remoteCall" href="/__debug__/sql_select/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Sel</a>
+							<a class="remoteCall" href="/__debug__/sql_explain/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Expl</a>
 							{% ifequal query.engine 'mysql' %}
-								<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}">Prof</a>
+								<a class="remoteCall" href="/__debug__/sql_profile/?sql={{ query.raw_sql|urlencode }}&amp;params={{ query.params|urlencode }}&amp;duration={{ query.duration|floatformat:"2"|urlencode }}&amp;hash={{ query.hash }}&amp;alias={{ query.alias|urlencode }}">Prof</a>
 							{% endifequal %}
 						{% endif %}
 					{% endif %}
@@ -55,12 +56,12 @@
 					<td colspan="2"></td>
 					<td colspan="4">
 						<div class="djSQLDetailsDiv">
-							<p><strong>Connection:</strong> {{ query.alias }}</p>
+							<p><strong>{% trans "Connection:" %}</strong> {{ query.alias }}</p>
 							{% if query.iso_level %}
-								<p><strong>Isolation Level:</strong> {{ query.iso_level }}</p>
+								<p><strong>{% trans "Isolation level:" %}</strong> {{ query.iso_level }}</p>
 							{% endif %}
 							{% if query.trans_status %}
-								<p><strong>Transaction Status:</strong> {{ query.trans_status }}</p>
+								<p><strong>{% trans "Transaction status:" %}</strong> {{ query.trans_status }}</p>
 							{% endif %}
 							{% if query.stacktrace %}
 								<pre class="stack">{{ query.stacktrace }}</pre>
@@ -74,7 +75,7 @@
 									</tr>
 									{% endfor %}
 								</table>
-								<p><strong>{{ query.template_info.name|default:"(unknown)" }}</strong></p>
+								<p><strong>{{ query.template_info.name|default:_("(unknown)") }}</strong></p>
 							{% endif %}
 						</div>
 					</td>
@@ -83,5 +84,5 @@
 		</tbody>
 	</table>
 {% else %}
-	<p>No SQL queries were recorded during this request.</p>
+	<p>{% trans "No SQL queries were recorded during this request." %}</p>
 {% endif %}

+ 2 - 0
templates/debug_toolbar/panels/sql_explain.html

@@ -10,6 +10,8 @@
 			<dd>{{ sql|safe }}</dd>
 			<dt>{% trans "Time" %}</dt>
 			<dd>{{ duration }} ms</dd>
+			<dt>{% trans "Database" %}</dt>
+			<dd>{{ alias }}</dd>
 		</dl>
 		<table class="djSqlExplain">
 			<thead>

+ 2 - 0
templates/debug_toolbar/panels/sql_profile.html

@@ -11,6 +11,8 @@
 				<dd>{{ sql|safe }}</dd>
 				<dt>{% trans "Time" %}</dt>
 				<dd>{{ duration }} ms</dd>
+				<dt>{% trans "Database" %}</dt>
+				<dd>{{ alias }}</dd>
 			</dl>
 			<table class="djSqlProfile">
 				<thead>

+ 2 - 0
templates/debug_toolbar/panels/sql_select.html

@@ -10,6 +10,8 @@
 			<dd>{{ sql|safe }}</dd>
 			<dt>{% trans "Time" %}</dt>
 			<dd>{{ duration }} ms</dd>
+			<dt>{% trans "Database" %}</dt>
+			<dd>{{ alias }}</dd>
 		</dl>
 		{% if result %}
 		<table class="djSqlSelect">

+ 4 - 4
templates/debug_toolbar/panels/templates.html

@@ -1,5 +1,5 @@
 {% load i18n %}
-<h4>{% trans 'Template path' %}{{ template_dirs|length|pluralize }}</h4>
+<h4>{% blocktrans count template_dirs|length as template_count %}Template path{% plural %}Template paths{% endblocktrans %}</h4>
 {% if template_dirs %}
 	<ol>
 	{% for template in template_dirs %}
@@ -7,10 +7,10 @@
 	{% endfor %}
 	</ol>
 {% else %}
-	<p>None</p>
+	<p>{% trans "None" %}</p>
 {% endif %}
 
-<h4>{% trans "Template" %}{{ templates|length|pluralize }}</h4>
+<h4>{% blocktrans count templates|length as template_count %}Template{% plural %}Templates{% endblocktrans %}</h4>
 {% if templates %}
 <dl>
 {% for template in templates %}
@@ -28,7 +28,7 @@
 	<p>{% trans 'None' %}</p>
 {% endif %}
 
-<h4>{% trans 'Context processor' %}{{ context_processors|length|pluralize }}</h4>
+<h4>{% blocktrans count context_processors|length as context_processors_count %}Context processor{% plural %}Context processors{% endblocktrans %}</h4>
 {% if context_processors %}
 <dl>
 {% for key, value in context_processors.iteritems %}

+ 1 - 2
templates/debug_toolbar/panels/versions.html

@@ -1,9 +1,8 @@
 {% load i18n %}
-
 <table>
 	<thead>
 		<tr>
-			<th>{% trans "Package" %}</th>
+			<th>{% trans "Name" %}</th>
 			<th>{% trans "Version" %}</th>
 		</tr>
 	</thead>

+ 4 - 1
templates/debug_toolbar/redirect.html

@@ -4,9 +4,12 @@
 </head>
 <body>
 <h1>HttpResponseRedirect</h1>
-<p>{% trans 'Location' %}: <a href="{{ redirect_to }}">{{ redirect_to }}</a></p>
+<p>{% trans 'Location' %}: <a id="redirect_to" href="{{ redirect_to }}">{{ redirect_to }}</a></p>
 <p class="notice">
 	{% trans "The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes.  You can click the above link to continue with the redirect as normal.  If you'd like to disable this feature, set the <code>DEBUG_TOOLBAR_CONFIG</code> dictionary's key <code>INTERCEPT_REDIRECTS</code> to <code>False</code>." %}
 </p>
+<script type="text/javascript">
+    document.getElementById('redirect_to').focus();
+</script>
 </body>
 </html>