wall coloured and split in days,
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 30 Nov 2011 13:43:35 +0000 (14:43 +0100)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Wed, 30 Nov 2011 13:43:35 +0000 (14:43 +0100)
emails mangled

18 files changed:
apps/catalogue/helpers.py
apps/catalogue/locale/pl/LC_MESSAGES/django.mo
apps/catalogue/locale/pl/LC_MESSAGES/django.po
apps/catalogue/templates/catalogue/activity.html
apps/catalogue/templates/catalogue/wall.html
apps/catalogue/templatetags/wall.py
apps/catalogue/urls.py
apps/catalogue/views.py
apps/email_mangler/__init__.py [new file with mode: 0644]
apps/email_mangler/locale/pl/LC_MESSAGES/django.mo [new file with mode: 0644]
apps/email_mangler/locale/pl/LC_MESSAGES/django.po [new file with mode: 0644]
apps/email_mangler/models.py [new file with mode: 0644]
apps/email_mangler/templatetags/__init__.py [new file with mode: 0755]
apps/email_mangler/templatetags/email.py [new file with mode: 0755]
redakcja/settings/common.py
redakcja/settings/compress.py
redakcja/static/css/filelist.css
redakcja/static/email_mangler/email_mangler.js [new file with mode: 0755]

index 7bc2481..df64ade 100644 (file)
@@ -1,3 +1,4 @@
+from datetime import date
 from functools import wraps
 
 from django.db.models import Count
@@ -28,3 +29,10 @@ def cached_in_field(field_name):
             return value
         return wrapped
     return decorator
+
+
+def parse_isodate(isodate):
+    try:
+        return date(*[int(p) for p in isodate.split('-')])
+    except (AttributeError, TypeError, ValueError):
+        raise ValueError("Not a date in ISO format.")
index b05c6cf..878689e 100644 (file)
Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ
index 293fd44..4378dcf 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-10-24 13:25+0200\n"
-"PO-Revision-Date: 2011-10-24 13:28+0100\n"
+"POT-Creation-Date: 2011-11-30 13:01+0100\n"
+"PO-Revision-Date: 2011-11-30 13:06+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
 "Language: pl\n"
@@ -18,7 +18,6 @@ msgstr ""
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
 
 #: forms.py:39
-#, fuzzy
 msgid "Text file must be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
@@ -38,25 +37,25 @@ msgstr "Katalogi zawierają dokumenty w częściach"
 msgid "Assigned to"
 msgstr "Przypisane do"
 
-#: forms.py:96
-#: forms.py:110
+#: forms.py:97
+#: forms.py:111
 msgid "Chunk with this slug already exists"
 msgstr "Część z tym slugiem już istnieje"
 
-#: forms.py:119
+#: forms.py:120
 msgid "Append to"
 msgstr "Dołącz do"
 
-#: views.py:149
+#: views.py:162
 #, python-format
 msgid "Slug already used for %s"
 msgstr "Slug taki sam jak dla pliku %s"
 
-#: views.py:151
+#: views.py:164
 msgid "Slug already used in repository."
 msgstr "Dokument o tym slugu już istnieje w repozytorium."
 
-#: views.py:157
+#: views.py:170
 msgid "File should be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
@@ -96,27 +95,27 @@ msgstr "książka"
 msgid "books"
 msgstr "książki"
 
-#: models/book.py:198
+#: models/book.py:222
 msgid "No chunks in the book."
 msgstr "Książka nie ma części."
 
-#: models/book.py:202
+#: models/book.py:226
 msgid "Not all chunks have publishable revisions."
 msgstr "Niektóre części nie są gotowe do publikacji."
 
-#: models/book.py:208
+#: models/book.py:232
 msgid "Invalid XML"
 msgstr "Nieprawidłowy XML"
 
-#: models/book.py:210
+#: models/book.py:234
 msgid "No Dublin Core found."
 msgstr "Brak sekcji Dublin Core."
 
-#: models/book.py:212
+#: models/book.py:236
 msgid "Invalid Dublin Core"
 msgstr "Nieprawidłowy Dublin Core"
 
-#: models/book.py:215
+#: models/book.py:239
 msgid "rdf:about is not"
 msgstr "rdf:about jest różny od"
 
@@ -141,6 +140,7 @@ msgid "time"
 msgstr "czas"
 
 #: models/publish_log.py:19
+#: templates/catalogue/wall.html:17
 msgid "user"
 msgstr "użytkownik"
 
@@ -165,6 +165,11 @@ msgstr "zapis publikacji części"
 msgid "chunk publish records"
 msgstr "zapisy publikacji części"
 
+#: templates/catalogue/activity.html:10
+#: templatetags/catalogue.py:29
+msgid "Activity"
+msgstr "Aktywność"
+
 #: templates/catalogue/base.html:8
 msgid "Platforma Redakcyjna"
 msgstr "Platforma Redakcyjna"
@@ -188,7 +193,7 @@ msgid "Chunks"
 msgstr "Części"
 
 #: templates/catalogue/book_detail.html:42
-#: templatetags/wall.py:67
+#: templatetags/wall.py:78
 msgid "Publication"
 msgstr "Publikacja"
 
@@ -224,6 +229,18 @@ msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:"
 msgid "Comments"
 msgstr "Komentarze"
 
+#: templates/catalogue/book_html.html:21
+msgid "Table of contents"
+msgstr "Spis treści"
+
+#: templates/catalogue/book_html.html:22
+msgid "Edit. note"
+msgstr "Nota red."
+
+#: templates/catalogue/book_html.html:23
+msgid "Infobox"
+msgstr "Informacje"
+
 #: templates/catalogue/chunk_add.html:5
 #: templates/catalogue/chunk_edit.html:19
 msgid "Split chunk"
@@ -310,6 +327,14 @@ msgstr "Ostatnia aktywność dla:"
 msgid "Users"
 msgstr "Użytkownicy"
 
+#: templates/catalogue/wall.html:27
+msgid "not logged in"
+msgstr "nie zalogowany"
+
+#: templates/catalogue/wall.html:32
+msgid "No activity recorded."
+msgstr "Nie zanotowano aktywności."
+
 #: templates/catalogue/book_list/book.html:6
 #: templates/catalogue/book_list/book.html:25
 msgid "Book settings"
@@ -376,10 +401,6 @@ msgstr "puste"
 msgid "My page"
 msgstr "Moja strona"
 
-#: templatetags/catalogue.py:29
-msgid "Activity"
-msgstr "Aktywność"
-
 #: templatetags/catalogue.py:30
 msgid "All"
 msgstr "Wszystkie"
@@ -388,22 +409,21 @@ msgstr "Wszystkie"
 msgid "Add"
 msgstr "Dodaj"
 
-#: templatetags/catalogue.py:38
-msgid "Admin"
-msgstr "Administracja"
-
-#: templatetags/wall.py:43
+#: templatetags/wall.py:49
 msgid "Related edit"
 msgstr "Powiązana zmiana"
 
-#: templatetags/wall.py:45
+#: templatetags/wall.py:51
 msgid "Edit"
 msgstr "Zmiana"
 
-#: templatetags/wall.py:84
+#: templatetags/wall.py:99
 msgid "Comment"
 msgstr "Komentarz"
 
+#~ msgid "Admin"
+#~ msgstr "Administracja"
+
 #~ msgid "edit"
 #~ msgstr "edytuj"
 
index 4354b33..9c2eac5 100755 (executable)
@@ -1,7 +1,17 @@
 {% extends "catalogue/base.html" %}
-
+{% load i18n %}
+{% load url from future %}
 {% load wall %}
 
-{% block leftcolumn %}
-    {% wall %}
-{% endblock leftcolumn %}
+
+{% block content %}
+
+<h1><a href='{% url "catalogue_activity" prev_day.isoformat %}'>&lt;</a>
+    {% trans "Activity" %}: {{ day }}
+    {% if next_day %}
+        <a href='{% url "catalogue_activity" next_day.isoformat %}'>&gt;</a>
+    {% endif %}
+</h1>
+
+    {% day_wall day %}
+{% endblock content %}
index 2ec3c0a..9227ba1 100755 (executable)
@@ -1,32 +1,35 @@
 {% load i18n %}
 {% load gravatar %}
+{% load email %}
 
-<ul class='wall' style='padding-left: 0; list-style: none;'>
+<ul class='wall'>
 {% for item in wall %}
-    <li style='clear: left; border-top: 1px dotted gray;  padding-bottom:1em; margin-bottom: 1em;'>
-        <div style='float: left;margin-right: 1em;'>
+    <li class="{{ item.tag }}{% if not item.user %} anonymous{% endif %}">
+        <div class='gravatar'>
             {% if item.get_email %}
                 {% gravatar_img_for_email item.get_email 32 %}
                 <br/>
             {% endif %}
-
-            <!--img src='{{ STATIC_URL }}img/wall/{{ item.tag}}.png' alt='{% trans item.tag %}' /-->
         </div>
 
-        <span style='float:right'>{{ item.timestamp }}</span>
+        <div class="time">{{ item.timestamp }}</div>
         <h3>{{ item.header }}</h3>
+        <a target="_blank" href='{{ item.url }}'>{{ item.title }}</a>
+        <br/><strong>{% trans "user" %}:</strong>
         {% if item.user %}
             <a href="{% url catalogue_user item.user.username %}">
             {{ item.user.first_name }} {{ item.user.last_name }}</a>
-            &lt;{{ item.user.email }}>
+            &lt;{{ item.user.email|email_link }}>
         {% else %}
             {{ item.user_name }}
-            {% if item.get_email %}
-                &lt;{{ item.get_email }}>
+            {% if item.email %}
+                &lt;{{ item.email|email_link }}>
             {% endif %}
+            ({% trans "not logged in" %})
         {% endif %}
-        <br/><a target="_blank" href='{{ item.url }}'>{{ item.title }}</a>
-        <br/>{{ item.summary }}
-        </li>
+        <br/>{{ item.summary|linebreaksbr }}
+    </li>
+{% empty %}
+    <li>{% trans "No activity recorded." %}</li>
 {% endfor %}
 </ul>
index b3e94df..28671fb 100755 (executable)
@@ -1,5 +1,6 @@
 from __future__ import absolute_import
 
+from datetime import timedelta
 from django.db.models import Q
 from django.core.urlresolvers import reverse
 from django.contrib.comments.models import Comment
@@ -17,6 +18,7 @@ class WallItem(object):
     url = ''
     timestamp = ''
     user = None
+    user_name = ''
     email = ''
 
     def __init__(self, tag):
@@ -29,12 +31,16 @@ class WallItem(object):
             return self.email
 
 
-def changes_wall(user, max_len):
+def changes_wall(user=None, max_len=None, day=None):
     qs = Chunk.change_model.objects.order_by('-created_at')
     qs = qs.select_related('author', 'tree', 'tree__book__title')
-    if user:
+    if user is not None:
         qs = qs.filter(Q(author=user) | Q(tree__user=user))
-    qs = qs[:max_len]
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(created_at__gte=day, created_at__lt=next_day)
     for item in qs:
         tag = 'stage' if item.tags.count() else 'change'
         chunk = item.tree
@@ -49,6 +55,7 @@ def changes_wall(user, max_len):
                 args=[chunk.book.slug, chunk.slug]) + '?diff=%d' % item.revision
         w.timestamp = item.created_at
         w.user = item.author
+        w.user_name = item.author_name
         w.email = item.author_email
         yield w
 
@@ -56,12 +63,16 @@ def changes_wall(user, max_len):
 # TODO: marked for publishing
 
 
-def published_wall(user, max_len):
+def published_wall(user=None, max_len=None, day=None):
     qs = BookPublishRecord.objects.select_related('book__title')
     if user:
         # TODO: published my book
         qs = qs.filter(Q(user=user))
-    qs = qs[:max_len]
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(timestamp__gte=day, timestamp__lt=next_day)
     for item in qs:
         w = WallItem('publish')
         w.header = _('Publication')
@@ -73,12 +84,16 @@ def published_wall(user, max_len):
         yield w
 
 
-def comments_wall(user, max_len):
+def comments_wall(user=None, max_len=None, day=None):
     qs = Comment.objects.filter(is_public=True).select_related().order_by('-submit_date')
     if user:
         # TODO: comments concerning my books
         qs = qs.filter(Q(user=user))
-    qs = qs[:max_len]
+    if max_len is not None:
+        qs = qs[:max_len]
+    if day is not None:
+        next_day = day + timedelta(1)
+        qs = qs.filter(submit_date__gte=day, submit_date__lt=next_day)
     for item in qs:
         w  = WallItem('comment')
         w.header = _('Comment')
@@ -87,22 +102,26 @@ def comments_wall(user, max_len):
         w.url = item.content_object.get_absolute_url()
         w.timestamp = item.submit_date
         w.user = item.user
-        w.email = item.user_email
+        ui = item.userinfo
+        w.email = item.email
+        w.user_name = item.name
         yield w
 
 
-def big_wall(max_len, *args):
+def big_wall(walls, max_len=None):
     """
         Takes some WallItem iterators and zips them into one big wall.
         Input iterators must already be sorted by timestamp.
     """
     subwalls = []
-    for w in args:
+    for w in walls:
         try:
             subwalls.append([next(w), w])
         except StopIteration:
             pass
 
+    if max_len is None:
+        max_len = -1
     while max_len and subwalls:
         i, next_item = max(enumerate(subwalls), key=lambda x: x[1][0].timestamp)
         yield next_item[0]
@@ -118,8 +137,19 @@ def wall(context, user=None, max_len=100):
     return {
         "request": context['request'],
         "STATIC_URL": context['STATIC_URL'],
-        "wall": big_wall(max_len,
+        "wall": big_wall([
             changes_wall(user, max_len),
             published_wall(user, max_len),
             comments_wall(user, max_len),
-        )}
+        ], max_len)}
+
+@register.inclusion_tag("catalogue/wall.html", takes_context=True)
+def day_wall(context, day):
+    return {
+        "request": context['request'],
+        "STATIC_URL": context['STATIC_URL'],
+        "wall": big_wall([
+            changes_wall(day=day),
+            published_wall(day=day),
+            comments_wall(day=day),
+        ])}
index 9fbc587..f7d3761 100644 (file)
@@ -11,6 +11,8 @@ urlpatterns = patterns('catalogue.views',
     url(r'^user/(?P<username>[^/]+)/$', 'user', name='catalogue_user'),
     url(r'^users/$', 'users', name='catalogue_users'),
     url(r'^activity/$', 'activity', name='catalogue_activity'),
+    url(r'^activity/(?P<isodate>\d{4}-\d{2}-\d{2})/$', 
+        'activity', name='catalogue_activity'),
 
     url(r'^upload/$',
         'upload', name='catalogue_upload'),
index 58a9378..c7189d4 100644 (file)
@@ -1,4 +1,4 @@
-from datetime import datetime
+from datetime import datetime, date, timedelta
 import logging
 import os
 from StringIO import StringIO
@@ -70,8 +70,20 @@ def users(request):
 
 
 @active_tab('activity')
-def activity(request):
-    return render(request, 'catalogue/activity.html')
+def activity(request, isodate=None):
+    today = date.today()
+    try:
+        day = helpers.parse_isodate(isodate)
+    except ValueError:
+        day = today
+
+    if day > today:
+        raise Http404
+    if day != today:
+        next_day = day + timedelta(1)
+    prev_day = day - timedelta(1)
+
+    return render(request, 'catalogue/activity.html', locals())
 
 
 @never_cache
diff --git a/apps/email_mangler/__init__.py b/apps/email_mangler/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo b/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..ed20bfb
Binary files /dev/null and b/apps/email_mangler/locale/pl/LC_MESSAGES/django.mo differ
diff --git a/apps/email_mangler/locale/pl/LC_MESSAGES/django.po b/apps/email_mangler/locale/pl/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..046b883
--- /dev/null
@@ -0,0 +1,27 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-11-30 14:27+0100\n"
+"PO-Revision-Date: 2011-11-30 14:27+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: templatetags/email.py:17
+msgid "at"
+msgstr "na"
+
+#: templatetags/email.py:18
+msgid "dot"
+msgstr "kropka"
+
diff --git a/apps/email_mangler/models.py b/apps/email_mangler/models.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/apps/email_mangler/templatetags/__init__.py b/apps/email_mangler/templatetags/__init__.py
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/apps/email_mangler/templatetags/email.py b/apps/email_mangler/templatetags/email.py
new file mode 100755 (executable)
index 0000000..376117a
--- /dev/null
@@ -0,0 +1,25 @@
+from django.utils.html import escape
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext as _
+from django import template
+
+register = template.Library()
+
+
+@register.filter
+def email_link(email):
+    email_safe = escape(email)
+    try:
+        name, domain = email_safe.split('@', 1)
+    except ValueError:
+        return email
+
+    at = escape(_('at'))
+    dot = escape(_('dot'))
+    mangled = "%s %s %s" % (name, at, (' %s ' % dot).join(domain.split('.')))
+    return mark_safe("<a class='mangled' data-addr1='%(name)s' "
+        "data-addr2='%(domain)s'>%(mangled)s</a>" % {
+            'name': name.encode('rot13'),
+            'domain': domain.encode('rot13'),
+            'mangled': mangled,
+        })
index ae43f3e..1e16bb1 100644 (file)
@@ -122,6 +122,7 @@ INSTALLED_APPS = (
     'wiki',
     'toolbar',
     'apiclient',
+    'email_mangler',
 )
 
 LOGIN_REDIRECT_URL = '/documents/user'
index 60ce9e3..d4425c2 100644 (file)
@@ -62,6 +62,7 @@ COMPRESS_JS = {
         'source_filenames': (
                 'js/catalogue/catalogue.js',
                 'js/slugify.js',
+                'email_mangler/email_mangler.js',
         ),
         'output_filename': 'compressed/catalogue_scripts_?.js',
      }
index f517ea6..166def0 100644 (file)
@@ -184,3 +184,43 @@ a:hover {
 .book-list-user .user-column {
     display: none;
 }
+
+
+/* wall */
+.wall {
+    padding-left: 0;
+    list-style: none;
+}
+
+.wall li {
+    clear: left;
+    border-top: 1px dotted gray;
+    padding: 0 1em 2em 1em;
+    margin-bottom: 0;
+}
+
+.wall .gravatar {
+    float: left;
+    margin-right: 1em;
+    margin-left: -1em;
+}
+.wall h3 {
+    font-size: 1.25em;
+    margin-top: 0;
+}
+
+.wall .time {
+    /* float:right; */
+}
+
+.wall .anonymous {
+    background-color: #efa;
+}
+
+.wall .comment {
+    background-color: #dfc;
+}
+
+.wall .publish {
+    background-color: #fdc;
+}
\ No newline at end of file
diff --git a/redakcja/static/email_mangler/email_mangler.js b/redakcja/static/email_mangler/email_mangler.js
new file mode 100755 (executable)
index 0000000..03c1a91
--- /dev/null
@@ -0,0 +1,21 @@
+var rot13 = function(s){
+    return s.replace(/[a-zA-Z]/g, function(c){
+        return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);
+    });
+};
+
+(function($) {
+    $(function() {
+
+        $(".mangled").each(function() {
+            $this = $(this);
+            var email = rot13($this.attr('data-addr1')) + '@' +
+                rot13($this.attr('data-addr2'));
+            $this.attr('href', "mailto:" + email);
+            $this.html(email);
+        });
+
+
+    });
+})(jQuery);
+