+from datetime import date
from functools import wraps
from django.db.models import Count
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.")
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"
"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."
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."
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"
msgstr "czas"
#: models/publish_log.py:19
+#: templates/catalogue/wall.html:17
msgid "user"
msgstr "użytkownik"
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"
msgstr "Części"
#: templates/catalogue/book_detail.html:42
-#: templatetags/wall.py:67
+#: templatetags/wall.py:78
msgid "Publication"
msgstr "Publikacja"
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"
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"
msgid "My page"
msgstr "Moja strona"
-#: templatetags/catalogue.py:29
-msgid "Activity"
-msgstr "Aktywność"
-
#: templatetags/catalogue.py:30
msgid "All"
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"
{% 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 %}'><</a>
+ {% trans "Activity" %}: {{ day }}
+ {% if next_day %}
+ <a href='{% url "catalogue_activity" next_day.isoformat %}'>></a>
+ {% endif %}
+</h1>
+
+ {% day_wall day %}
+{% endblock content %}
{% 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>
- <{{ 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 %}
- <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>
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
url = ''
timestamp = ''
user = None
+ user_name = ''
email = ''
def __init__(self, tag):
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
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
# 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')
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')
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]
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),
+ ])}
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'),
-from datetime import datetime
+from datetime import datetime, date, timedelta
import logging
import os
from StringIO import StringIO
@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
--- /dev/null
+# 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"
+
--- /dev/null
+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,
+ })
'wiki',
'toolbar',
'apiclient',
+ 'email_mangler',
)
LOGIN_REDIRECT_URL = '/documents/user'
'source_filenames': (
'js/catalogue/catalogue.js',
'js/slugify.js',
+ 'email_mangler/email_mangler.js',
),
'output_filename': 'compressed/catalogue_scripts_?.js',
}
.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
--- /dev/null
+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);
+