From: Radek Czajka Date: Wed, 30 Nov 2011 13:43:35 +0000 (+0100) Subject: wall coloured and split in days, X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/b0af42626632f8750ac9e88338be7e196bd4f91d wall coloured and split in days, emails mangled --- diff --git a/apps/catalogue/helpers.py b/apps/catalogue/helpers.py index 7bc24819..df64ade1 100644 --- a/apps/catalogue/helpers.py +++ b/apps/catalogue/helpers.py @@ -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.") diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo index b05c6cfa..878689e0 100644 Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.po b/apps/catalogue/locale/pl/LC_MESSAGES/django.po index 293fd449..4378dcf8 100644 --- a/apps/catalogue/locale/pl/LC_MESSAGES/django.po +++ b/apps/catalogue/locale/pl/LC_MESSAGES/django.po @@ -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 \n" "Language-Team: Fundacja Nowoczesna Polska \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" diff --git a/apps/catalogue/templates/catalogue/activity.html b/apps/catalogue/templates/catalogue/activity.html index 4354b337..9c2eac51 100755 --- a/apps/catalogue/templates/catalogue/activity.html +++ b/apps/catalogue/templates/catalogue/activity.html @@ -1,7 +1,17 @@ {% extends "catalogue/base.html" %} - +{% load i18n %} +{% load url from future %} {% load wall %} -{% block leftcolumn %} - {% wall %} -{% endblock leftcolumn %} + +{% block content %} + +

< + {% trans "Activity" %}: {{ day }} + {% if next_day %} + > + {% endif %} +

+ + {% day_wall day %} +{% endblock content %} diff --git a/apps/catalogue/templates/catalogue/wall.html b/apps/catalogue/templates/catalogue/wall.html index 2ec3c0ac..9227ba19 100755 --- a/apps/catalogue/templates/catalogue/wall.html +++ b/apps/catalogue/templates/catalogue/wall.html @@ -1,32 +1,35 @@ {% load i18n %} {% load gravatar %} +{% load email %} -
    +
      {% for item in wall %} -
    • -
      +
    • +
      {% if item.get_email %} {% gravatar_img_for_email item.get_email 32 %}
      {% endif %} - -
      - {{ item.timestamp }} +
      {{ item.timestamp }}

      {{ item.header }}

      + {{ item.title }} +
      {% trans "user" %}: {% if item.user %} {{ item.user.first_name }} {{ item.user.last_name }} - <{{ item.user.email }}> + <{{ item.user.email|email_link }}> {% else %} {{ item.user_name }} - {% if item.get_email %} - <{{ item.get_email }}> + {% if item.email %} + <{{ item.email|email_link }}> {% endif %} + ({% trans "not logged in" %}) {% endif %} -
      {{ item.title }} -
      {{ item.summary }} -
    • +
      {{ item.summary|linebreaksbr }} + +{% empty %} +
    • {% trans "No activity recorded." %}
    • {% endfor %}
    diff --git a/apps/catalogue/templatetags/wall.py b/apps/catalogue/templatetags/wall.py index b3e94dfe..28671fb7 100755 --- a/apps/catalogue/templatetags/wall.py +++ b/apps/catalogue/templatetags/wall.py @@ -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), + ])} diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index 9fbc587d..f7d37616 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -11,6 +11,8 @@ urlpatterns = patterns('catalogue.views', url(r'^user/(?P[^/]+)/$', 'user', name='catalogue_user'), url(r'^users/$', 'users', name='catalogue_users'), url(r'^activity/$', 'activity', name='catalogue_activity'), + url(r'^activity/(?P\d{4}-\d{2}-\d{2})/$', + 'activity', name='catalogue_activity'), url(r'^upload/$', 'upload', name='catalogue_upload'), diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index 58a9378e..c7189d47 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -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 index 00000000..e69de29b 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 index 00000000..ed20bfb8 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 index 00000000..046b8835 --- /dev/null +++ b/apps/email_mangler/locale/pl/LC_MESSAGES/django.po @@ -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 , 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 \n" +"Language-Team: LANGUAGE \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 index 00000000..e69de29b diff --git a/apps/email_mangler/templatetags/__init__.py b/apps/email_mangler/templatetags/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/apps/email_mangler/templatetags/email.py b/apps/email_mangler/templatetags/email.py new file mode 100755 index 00000000..376117a8 --- /dev/null +++ b/apps/email_mangler/templatetags/email.py @@ -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("%(mangled)s" % { + 'name': name.encode('rot13'), + 'domain': domain.encode('rot13'), + 'mangled': mangled, + }) diff --git a/redakcja/settings/common.py b/redakcja/settings/common.py index ae43f3e2..1e16bb1e 100644 --- a/redakcja/settings/common.py +++ b/redakcja/settings/common.py @@ -122,6 +122,7 @@ INSTALLED_APPS = ( 'wiki', 'toolbar', 'apiclient', + 'email_mangler', ) LOGIN_REDIRECT_URL = '/documents/user' diff --git a/redakcja/settings/compress.py b/redakcja/settings/compress.py index 60ce9e36..d4425c21 100644 --- a/redakcja/settings/compress.py +++ b/redakcja/settings/compress.py @@ -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', } diff --git a/redakcja/static/css/filelist.css b/redakcja/static/css/filelist.css index f517ea6e..166def07 100644 --- a/redakcja/static/css/filelist.css +++ b/redakcja/static/css/filelist.css @@ -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 index 00000000..03c1a915 --- /dev/null +++ b/redakcja/static/email_mangler/email_mangler.js @@ -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); +