From: Radek Czajka Date: Wed, 26 Mar 2014 15:32:58 +0000 (+0100) Subject: Merge with master. X-Git-Url: https://git.mdrn.pl/redakcja.git/commitdiff_plain/ec5b493bff5efa683aadf5f78aa891eff3550156?hp=0f84fb864cfe3f6c642706f40933ede6a74fe0c4 Merge with master. --- diff --git a/apps/apiclient/__init__.py b/apps/apiclient/__init__.py index 376b66e6..33d20081 100644 --- a/apps/apiclient/__init__.py +++ b/apps/apiclient/__init__.py @@ -46,5 +46,5 @@ def api_call(user, path, data=None): elif status == '401': raise ApiError('User not authorized for publishing.') else: - raise ApiError("WL API call error [code %s]" % status) + raise ApiError("WL API call error %s, path: %s" % (status, path)) diff --git a/apps/catalogue/admin.py b/apps/catalogue/admin.py index 7fbacad9..53e8a256 100644 --- a/apps/catalogue/admin.py +++ b/apps/catalogue/admin.py @@ -12,5 +12,7 @@ class BookAdmin(admin.ModelAdmin): admin.site.register(models.Project) admin.site.register(models.Book, BookAdmin) admin.site.register(models.Chunk) - admin.site.register(models.Chunk.tag_model) + +admin.site.register(models.Image) +admin.site.register(models.Image.tag_model) diff --git a/apps/catalogue/forms.py b/apps/catalogue/forms.py index 7ae7ff40..83b16523 100644 --- a/apps/catalogue/forms.py +++ b/apps/catalogue/forms.py @@ -9,7 +9,7 @@ from django import forms from django.utils.translation import ugettext_lazy as _ from catalogue.constants import MASTERS -from catalogue.models import Book, Chunk +from catalogue.models import Book, Chunk, Image class DocumentCreateForm(forms.ModelForm): """ @@ -145,7 +145,7 @@ class ReadonlyBookForm(BookForm): def __init__(self, *args, **kwargs): ret = super(ReadonlyBookForm, self).__init__(*args, **kwargs) for field in self.fields.values(): - field.widget.attrs.update({"readonly": True}) + field.widget.attrs.update({"disabled": "disabled"}) return ret @@ -155,3 +155,30 @@ class ChooseMasterForm(forms.Form): """ master = forms.ChoiceField(choices=((m, m) for m in MASTERS)) + + +class ImageForm(forms.ModelForm): + """Form used for editing an Image.""" + user = forms.ModelChoiceField(queryset= + User.objects.annotate(count=Count('chunk')). + order_by('-count', 'last_name', 'first_name'), required=False, + label=_('Assigned to')) + + class Meta: + model = Image + fields = ['title', 'slug', 'user', 'stage'] + + def __init__(self, *args, **kwargs): + super(ImageForm, self).__init__(*args, **kwargs) + self.fields['slug'].widget.attrs={'class': 'autoslug'} + self.fields['title'].widget.attrs={'class': 'autoslug-source'} + + +class ReadonlyImageForm(ImageForm): + """Form used for not editing a Book.""" + + def __init__(self, *args, **kwargs): + ret = super(ReadonlyImageForm, self).__init__(*args, **kwargs) + for field in self.fields.values(): + field.widget.attrs.update({"disabled": "disabled"}) + return ret diff --git a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo index b6a12e32..64f78b46 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 6790400c..d98440f3 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: 2013-07-16 13:22+0200\n" -"PO-Revision-Date: 2013-07-16 13:22+0100\n" +"POT-Creation-Date: 2014-03-26 16:13+0100\n" +"PO-Revision-Date: 2014-03-26 16:14+0100\n" "Last-Translator: Radek Czajka \n" "Language-Team: Fundacja Nowoczesna Polska \n" @@ -36,7 +36,7 @@ msgstr "Plik ZIP" msgid "Directories are documents in chunks" msgstr "Katalogi zawierają dokumenty w częściach" -#: forms.py:76 +#: forms.py:76 forms.py:165 msgid "Assigned to" msgstr "Przypisane do" @@ -48,36 +48,36 @@ msgstr "Część z tym slugiem już istnieje" msgid "Append to" msgstr "Dołącz do" -#: views.py:160 +#: views.py:166 #, python-format msgid "Slug already used for %s" msgstr "Slug taki sam jak dla pliku %s" -#: views.py:162 +#: views.py:168 msgid "Slug already used in repository." msgstr "Dokument o tym slugu już istnieje w repozytorium." -#: views.py:168 +#: views.py:174 msgid "File should be UTF-8 encoded." msgstr "Plik powinien mieć kodowanie UTF-8." -#: views.py:498 models/book.py:56 +#: views.py:552 models/book.py:56 msgid "books" msgstr "książki" -#: views.py:500 +#: views.py:554 msgid "scan gallery" msgstr "galeria skanów" -#: models/book.py:28 models/chunk.py:23 +#: models/book.py:28 models/chunk.py:23 models/image.py:21 msgid "title" msgstr "tytuł" -#: models/book.py:29 models/chunk.py:24 +#: models/book.py:29 models/chunk.py:24 models/image.py:22 msgid "slug" msgstr "slug" -#: models/book.py:30 +#: models/book.py:30 models/image.py:23 msgid "public" msgstr "publiczna" @@ -105,19 +105,19 @@ msgstr "Książka nie ma części." msgid "Not all chunks have publishable revisions." msgstr "Niektóre części nie są gotowe do publikacji." -#: models/book.py:266 +#: models/book.py:266 models/image.py:80 msgid "Invalid XML" msgstr "Nieprawidłowy XML" -#: models/book.py:268 +#: models/book.py:268 models/image.py:82 msgid "No Dublin Core found." msgstr "Brak sekcji Dublin Core." -#: models/book.py:270 +#: models/book.py:270 models/image.py:84 msgid "Invalid Dublin Core" msgstr "Nieprawidłowy Dublin Core" -#: models/book.py:273 +#: models/book.py:273 models/image.py:88 msgid "rdf:about is not" msgstr "rdf:about jest różny od" @@ -137,6 +137,18 @@ msgstr "część" msgid "chunks" msgstr "części" +#: models/image.py:20 models/image.py:34 models/publish_log.py:45 +msgid "image" +msgstr "obraz" + +#: models/image.py:35 +msgid "images" +msgstr "obrazy" + +#: models/image.py:73 +msgid "There is no publishable revision" +msgstr "Żadna wersja nie została oznaczona do publikacji." + #: models/project.py:13 msgid "name" msgstr "nazwa" @@ -145,7 +157,7 @@ msgstr "nazwa" msgid "notes" msgstr "notatki" -#: models/project.py:19 templates/catalogue/book_list/book_list.html:62 +#: models/project.py:19 templates/catalogue/book_list/book_list.html:64 msgid "project" msgstr "projekt" @@ -153,11 +165,12 @@ msgstr "projekt" msgid "projects" msgstr "projekty" -#: models/publish_log.py:18 +#: models/publish_log.py:18 models/publish_log.py:46 msgid "time" msgstr "czas" -#: models/publish_log.py:19 templates/catalogue/wall.html:18 +#: models/publish_log.py:19 models/publish_log.py:47 +#: templates/catalogue/wall.html:19 msgid "user" msgstr "użytkownik" @@ -169,7 +182,7 @@ msgstr "zapis publikacji książki" msgid "book publish records" msgstr "zapisy publikacji książek" -#: models/publish_log.py:34 +#: models/publish_log.py:34 models/publish_log.py:48 msgid "change" msgstr "zmiana" @@ -181,7 +194,16 @@ msgstr "zapis publikacji części" msgid "chunk publish records" msgstr "zapisy publikacji części" -#: templates/catalogue/activity.html:9 templatetags/catalogue.py:29 +#: models/publish_log.py:53 +msgid "image publish record" +msgstr "zapis publikacji obrazu" + +#: models/publish_log.py:54 +msgid "image publish records" +msgstr "zapisy publikacji obrazów" + +#: templates/catalogue/activity.html:6 templates/catalogue/activity.html:12 +#: templatetags/catalogue.py:29 msgid "Activity" msgstr "Aktywność" @@ -189,118 +211,145 @@ msgstr "Aktywność" msgid "Platforma Redakcyjna" msgstr "Platforma Redakcyjna" -#: templates/catalogue/book_append_to.html:9 +#: templates/catalogue/book_append_to.html:4 +#: templates/catalogue/book_append_to.html:11 msgid "Append book" msgstr "Dołącz książkę" -#: templates/catalogue/book_detail.html:14 -#: templates/catalogue/book_edit.html:9 templates/catalogue/chunk_edit.html:12 +#: templates/catalogue/book_detail.html:18 +#: templates/catalogue/book_edit.html:13 +#: templates/catalogue/chunk_edit.html:16 +#: templates/catalogue/image_detail.html:18 msgid "Save" msgstr "Zapisz" -#: templates/catalogue/book_detail.html:21 +#: templates/catalogue/book_detail.html:25 msgid "Edit gallery" msgstr "Edytuj galerię" -#: templates/catalogue/book_detail.html:24 +#: templates/catalogue/book_detail.html:28 msgid "Append to other book" msgstr "Dołącz do innej książki" -#: templates/catalogue/book_detail.html:30 +#: templates/catalogue/book_detail.html:34 msgid "Chunks" msgstr "Części" -#: templates/catalogue/book_detail.html:45 templatetags/wall.py:78 +#: templates/catalogue/book_detail.html:49 +#: templates/catalogue/image_detail.html:36 templatetags/wall.py:78 msgid "Publication" msgstr "Publikacja" -#: templates/catalogue/book_detail.html:54 +#: templates/catalogue/book_detail.html:58 +#: templates/catalogue/image_detail.html:38 msgid "Last published" msgstr "Ostatnio opublikowano" -#: templates/catalogue/book_detail.html:64 +#: templates/catalogue/book_detail.html:68 msgid "Full XML" msgstr "Pełny XML" -#: templates/catalogue/book_detail.html:65 +#: templates/catalogue/book_detail.html:69 msgid "HTML version" msgstr "Wersja HTML" -#: templates/catalogue/book_detail.html:66 +#: templates/catalogue/book_detail.html:70 msgid "TXT version" msgstr "Wersja TXT" -#: templates/catalogue/book_detail.html:67 +#: templates/catalogue/book_detail.html:71 msgid "PDF version" msgstr "Wersja PDF" -#: templates/catalogue/book_detail.html:68 +#: templates/catalogue/book_detail.html:72 msgid "EPUB version" msgstr "Wersja EPUB" -#: templates/catalogue/book_detail.html:81 +#: templates/catalogue/book_detail.html:85 +#: templates/catalogue/image_detail.html:57 msgid "Publish" msgstr "Opublikuj" -#: templates/catalogue/book_detail.html:85 +#: templates/catalogue/book_detail.html:89 +#: templates/catalogue/image_detail.html:61 msgid "Log in to publish." msgstr "Zaloguj się, aby opublikować." -#: templates/catalogue/book_detail.html:88 +#: templates/catalogue/book_detail.html:92 +#: templates/catalogue/image_detail.html:64 msgid "This book can't be published yet, because:" msgstr "Ta książka nie może jeszcze zostać opublikowana. Powód:" -#: templates/catalogue/book_detail.html:98 +#: templates/catalogue/book_detail.html:102 +#: templates/catalogue/image_detail.html:72 msgid "Comments" msgstr "Komentarze" -#: templates/catalogue/book_text.html:7 -msgid "Redakcja" -msgstr "" +#: templates/catalogue/book_edit.html:5 +msgid "Edit book" +msgstr "Edytuj książkę" -#: templates/catalogue/book_text.html:15 +#: templates/catalogue/book_html.html:13 templates/catalogue/book_text.html:15 msgid "Table of contents" msgstr "Spis treści" -#: templates/catalogue/book_text.html:17 +#: templates/catalogue/book_html.html:14 templates/catalogue/book_text.html:17 msgid "Edit. note" msgstr "Nota red." -#: templates/catalogue/chunk_add.html:5 templates/catalogue/chunk_edit.html:18 +#: templates/catalogue/book_html.html:15 +msgid "Infobox" +msgstr "Informacje" + +#: templates/catalogue/book_text.html:7 +msgid "Redakcja" +msgstr "" + +#: templates/catalogue/chunk_add.html:5 templates/catalogue/chunk_add.html:9 +#: templates/catalogue/chunk_edit.html:22 msgid "Split chunk" msgstr "Podziel część" -#: templates/catalogue/chunk_add.html:10 +#: templates/catalogue/chunk_add.html:14 msgid "Insert empty chunk after" msgstr "Wstaw pustą część po" -#: templates/catalogue/chunk_add.html:13 +#: templates/catalogue/chunk_add.html:17 msgid "Add chunk" msgstr "Dodaj część" -#: templates/catalogue/chunk_edit.html:5 +#: templates/catalogue/chunk_edit.html:5 templates/catalogue/chunk_edit.html:9 #: templates/catalogue/book_list/book.html:8 #: templates/catalogue/book_list/chunk.html:6 msgid "Chunk settings" msgstr "Ustawienia części" -#: templates/catalogue/chunk_edit.html:10 +#: templates/catalogue/chunk_edit.html:14 msgid "Book" msgstr "Książka" #: templates/catalogue/document_create_missing.html:5 +#: templates/catalogue/document_create_missing.html:9 msgid "Create a new book" msgstr "Utwórz nową książkę" -#: templates/catalogue/document_create_missing.html:11 +#: templates/catalogue/document_create_missing.html:15 msgid "Create book" msgstr "Utwórz książkę" -#: templates/catalogue/document_upload.html:8 -msgid "Bulk documents upload" +#: templates/catalogue/document_list.html:7 +msgid "Book list" +msgstr "Lista książek" + +#: templates/catalogue/document_upload.html:5 +msgid "Bulk document upload" msgstr "Hurtowe dodawanie dokumentów" #: templates/catalogue/document_upload.html:11 +msgid "Bulk documents upload" +msgstr "Hurtowe dodawanie dokumentów" + +#: templates/catalogue/document_upload.html:14 msgid "" "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with " ".xml will be ignored." @@ -308,91 +357,136 @@ msgstr "" "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie " "kończące się na .xml zostaną zignorowane." -#: templates/catalogue/document_upload.html:17 templatetags/catalogue.py:35 +#: templates/catalogue/document_upload.html:20 +#: templates/catalogue/upload_pdf.html:16 templatetags/catalogue.py:36 msgid "Upload" msgstr "Załaduj" -#: templates/catalogue/document_upload.html:24 +#: templates/catalogue/document_upload.html:27 msgid "" "There have been some errors. No files have been added to the repository." msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium." -#: templates/catalogue/document_upload.html:25 +#: templates/catalogue/document_upload.html:28 msgid "Offending files" msgstr "Błędne pliki" -#: templates/catalogue/document_upload.html:33 +#: templates/catalogue/document_upload.html:36 msgid "Correct files" msgstr "Poprawne pliki" -#: templates/catalogue/document_upload.html:44 +#: templates/catalogue/document_upload.html:47 msgid "Files have been successfully uploaded to the repository." msgstr "Pliki zostały dodane do repozytorium." -#: templates/catalogue/document_upload.html:45 +#: templates/catalogue/document_upload.html:48 msgid "Uploaded files" msgstr "Dodane pliki" -#: templates/catalogue/document_upload.html:55 +#: templates/catalogue/document_upload.html:58 msgid "Skipped files" msgstr "Pominięte pliki" -#: templates/catalogue/document_upload.html:56 +#: templates/catalogue/document_upload.html:59 msgid "Files skipped due to no .xml extension" msgstr "Pliki pominięte z powodu braku rozszerzenia .xml." -#: templates/catalogue/my_page.html:21 -msgid "Your last edited documents" -msgstr "Twoje ostatnie edycje" +#: templates/catalogue/image_detail.html:26 +msgid "Editor" +msgstr "Edytor" -#: templates/catalogue/my_page.html:30 templates/catalogue/user_page.html:13 -msgid "Recent activity for" -msgstr "Ostatnia aktywność dla:" +#: templates/catalogue/image_detail.html:28 +msgid "Proceed to the editor." +msgstr "Przejdź do edytora." -#: templates/catalogue/user_list.html:7 templatetags/catalogue.py:31 -msgid "Users" -msgstr "Użytkownicy" - -#: templates/catalogue/wall.html:28 -msgid "not logged in" -msgstr "nie zalogowany" +#: templates/catalogue/image_list.html:7 +msgid "Image list" +msgstr "Lista obrazów" -#: templates/catalogue/wall.html:33 -msgid "No activity recorded." -msgstr "Nie zanotowano aktywności." - -#: templates/catalogue/book_list/book.html:7 -#: templates/catalogue/book_list/book.html:28 -msgid "Book settings" -msgstr "Ustawienia książki" - -#: templates/catalogue/book_list/book_list.html:22 -msgid "Show hidden books" -msgstr "Pokaż ukryte książki" +#: templates/catalogue/image_short.html:4 +msgid "Image settings" +msgstr "Ustawienia obrazu" +#: templates/catalogue/image_table.html:19 #: templates/catalogue/book_list/book_list.html:27 msgid "Search in book titles" msgstr "Szukaj w tytułach książek" +#: templates/catalogue/image_table.html:24 #: templates/catalogue/book_list/book_list.html:32 msgid "stage" msgstr "etap" +#: templates/catalogue/image_table.html:26 +#: templates/catalogue/image_table.html:37 #: templates/catalogue/book_list/book_list.html:34 #: templates/catalogue/book_list/book_list.html:45 -#: templates/catalogue/book_list/book_list.html:64 +#: templates/catalogue/book_list/book_list.html:66 msgid "none" msgstr "brak" +#: templates/catalogue/image_table.html:35 #: templates/catalogue/book_list/book_list.html:43 msgid "editor" msgstr "redaktor" -#: templates/catalogue/book_list/book_list.html:54 +#: templates/catalogue/image_table.html:46 +#: templates/catalogue/book_list/book_list.html:56 msgid "status" msgstr "status" -#: templates/catalogue/book_list/book_list.html:88 +#: templates/catalogue/image_table.html:63 +#, python-format +msgid "%(c)s image" +msgid_plural "%(c)s images" +msgstr[0] "%(c)s obraz" +msgstr[1] "%(c)s obrazy" +msgstr[2] "%(c)s obrazów" + +#: templates/catalogue/image_table.html:68 +msgid "No images found." +msgstr "Nie znaleziono obrazów." + +#: templates/catalogue/my_page.html:15 templatetags/catalogue.py:27 +msgid "My page" +msgstr "Moja strona" + +#: templates/catalogue/my_page.html:24 +msgid "Your last edited documents" +msgstr "Twoje ostatnie edycje" + +#: templates/catalogue/my_page.html:33 templates/catalogue/user_page.html:16 +msgid "Recent activity for" +msgstr "Ostatnia aktywność dla:" + +#: templates/catalogue/upload_pdf.html:5 +#: templates/catalogue/upload_pdf.html:11 +msgid "PDF file upload" +msgstr "Ładowanie pliku PDF" + +#: templates/catalogue/user_list.html:6 templates/catalogue/user_list.html:11 +#: templatetags/catalogue.py:32 +msgid "Users" +msgstr "Użytkownicy" + +#: templates/catalogue/wall.html:29 +msgid "not logged in" +msgstr "nie zalogowany" + +#: templates/catalogue/wall.html:34 +msgid "No activity recorded." +msgstr "Nie zanotowano aktywności." + +#: templates/catalogue/book_list/book.html:7 +#: templates/catalogue/book_list/book.html:28 +msgid "Book settings" +msgstr "Ustawienia książki" + +#: templates/catalogue/book_list/book_list.html:22 +msgid "Show hidden books" +msgstr "Pokaż ukryte książki" + +#: templates/catalogue/book_list/book_list.html:90 #, python-format msgid "%(c)s book" msgid_plural "%(c)s books" @@ -400,67 +494,67 @@ msgstr[0] "%(c)s książka" msgstr[1] "%(c)s książki" msgstr[2] "%(c)s książek" -#: templates/catalogue/book_list/book_list.html:93 +#: templates/catalogue/book_list/book_list.html:95 msgid "No books found." msgstr "Nie znaleziono książek." -#: templates/catalogue/book_list/book_list.html:99 +#: templates/catalogue/book_list/book_list.html:101 msgid "Set stage" msgstr "Ustaw etap" -#: templates/catalogue/book_list/book_list.html:100 +#: templates/catalogue/book_list/book_list.html:102 msgid "Set user" msgstr "Przypisz redaktora" -#: templates/catalogue/book_list/book_list.html:102 +#: templates/catalogue/book_list/book_list.html:104 msgid "Project" msgstr "Projekt" -#: templates/catalogue/book_list/book_list.html:103 +#: templates/catalogue/book_list/book_list.html:105 msgid "Mark publishable" msgstr "Oznacz do publikacji" -#: templates/catalogue/book_list/book_list.html:104 +#: templates/catalogue/book_list/book_list.html:106 msgid "Mark not publishable" msgstr "Odznacz do publikacji" -#: templates/catalogue/book_list/book_list.html:105 +#: templates/catalogue/book_list/book_list.html:107 msgid "Other user" msgstr "Inny użytkownik" -#: templatetags/book_list.py:84 +#: templatetags/book_list.py:84 templatetags/book_list.py:152 msgid "publishable" msgstr "do publikacji" -#: templatetags/book_list.py:85 +#: templatetags/book_list.py:85 templatetags/book_list.py:153 msgid "changed" msgstr "zmienione" -#: templatetags/book_list.py:86 +#: templatetags/book_list.py:86 templatetags/book_list.py:154 msgid "published" msgstr "opublikowane" -#: templatetags/book_list.py:87 +#: templatetags/book_list.py:87 templatetags/book_list.py:155 msgid "unpublished" msgstr "nie opublikowane" -#: templatetags/book_list.py:88 +#: templatetags/book_list.py:88 templatetags/book_list.py:156 msgid "empty" msgstr "puste" -#: templatetags/catalogue.py:27 -msgid "My page" -msgstr "Moja strona" - #: templatetags/catalogue.py:30 msgid "All" msgstr "Wszystkie" -#: templatetags/catalogue.py:34 +#: templatetags/catalogue.py:31 +msgid "Images" +msgstr "Obrazy" + +#: templatetags/catalogue.py:35 msgid "Add" msgstr "Dodaj" -#: templatetags/catalogue.py:37 +#: templatetags/catalogue.py:38 msgid "Covers" msgstr "Okładki" @@ -476,9 +570,6 @@ msgstr "Zmiana" msgid "Comment" msgstr "Komentarz" -#~ msgid "Infobox" -#~ msgstr "Informacje" - #~ msgid "Admin" #~ msgstr "Administracja" diff --git a/apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py b/apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py new file mode 100644 index 00000000..599e103e --- /dev/null +++ b/apps/catalogue/migrations/0012_auto__add_imagepublishrecord__add_imagechange__add_unique_imagechange_.py @@ -0,0 +1,270 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'ImagePublishRecord' + db.create_table(u'catalogue_imagepublishrecord', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('image', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Image'])), + ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ImageChange'])), + )) + db.send_create_signal('catalogue', ['ImagePublishRecord']) + + # Adding model 'ImageChange' + db.create_table(u'catalogue_imagechange', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)), + ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)), + ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)), + ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ImageChange'])), + ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ImageChange'])), + ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)), + ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)), + ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Image'])), + ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + )) + db.send_create_signal('catalogue', ['ImageChange']) + + # Adding M2M table for field tags on 'ImageChange' + db.create_table(u'catalogue_imagechange_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('imagechange', models.ForeignKey(orm['catalogue.imagechange'], null=False)), + ('imagetag', models.ForeignKey(orm['catalogue.imagetag'], null=False)) + )) + db.create_unique(u'catalogue_imagechange_tags', ['imagechange_id', 'imagetag_id']) + + # Adding unique constraint on 'ImageChange', fields ['tree', 'revision'] + db.create_unique(u'catalogue_imagechange', ['tree_id', 'revision']) + + # Adding model 'ImageTag' + db.create_table(u'catalogue_imagetag', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('slug', self.gf('django.db.models.fields.SlugField')(max_length=64, unique=True, null=True, blank=True)), + ('ordering', self.gf('django.db.models.fields.IntegerField')()), + )) + db.send_create_signal('catalogue', ['ImageTag']) + + # Adding model 'Image' + db.create_table(u'catalogue_image', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + ('image', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)), + ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)), + ('public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)), + ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)), + ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)), + ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)), + ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)), + ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ImageTag'], null=True, blank=True)), + ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ImageChange'], null=True, blank=True)), + ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_image', null=True, to=orm['auth.User'])), + )) + db.send_create_signal('catalogue', ['Image']) + + + def backwards(self, orm): + # Removing unique constraint on 'ImageChange', fields ['tree', 'revision'] + db.delete_unique(u'catalogue_imagechange', ['tree_id', 'revision']) + + # Deleting model 'ImagePublishRecord' + db.delete_table(u'catalogue_imagepublishrecord') + + # Deleting model 'ImageChange' + db.delete_table(u'catalogue_imagechange') + + # Removing M2M table for field tags on 'ImageChange' + db.delete_table('catalogue_imagechange_tags') + + # Deleting model 'ImageTag' + db.delete_table(u'catalogue_imagetag') + + # Deleting model 'Image' + db.delete_table(u'catalogue_image') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'catalogue.book': { + 'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'}, + '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_on_track': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'dc_cover_image': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['cover.Image']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}), + 'dc_slug': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['catalogue.Book']"}), + 'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}), + 'project': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Project']", 'null': 'True', 'blank': 'True'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + 'catalogue.bookpublishrecord': { + 'Meta': {'ordering': "['-timestamp']", 'object_name': 'BookPublishRecord'}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Book']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + 'catalogue.chunk': { + 'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'}, + '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_hidden': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.Book']"}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_chunk'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'gallery_start': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True', 'blank': 'True'}), + 'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ChunkChange']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'number': ('django.db.models.fields.IntegerField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ChunkTag']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'catalogue.chunkchange': { + 'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ChunkChange'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ChunkChange']"}), + 'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ChunkTag']"}), + 'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Chunk']"}) + }, + 'catalogue.chunkpublishrecord': { + 'Meta': {'object_name': 'ChunkPublishRecord'}, + 'book_record': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.BookPublishRecord']"}), + 'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ChunkChange']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + 'catalogue.chunktag': { + 'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'ordering': ('django.db.models.fields.IntegerField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'catalogue.image': { + 'Meta': {'ordering': "['title']", 'object_name': 'Image'}, + '_changed': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_new_publishable': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_published': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}), + '_short_html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_image'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'stage': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['catalogue.ImageTag']", 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'catalogue.imagechange': { + 'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'ImageChange'}, + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'author_email': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'author_name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'}), + 'created_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), + 'data': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'merge_parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'merge_children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['catalogue.ImageChange']"}), + 'publishable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'change_set'", 'symmetrical': 'False', 'to': "orm['catalogue.ImageTag']"}), + 'tree': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'change_set'", 'to': "orm['catalogue.Image']"}) + }, + 'catalogue.imagepublishrecord': { + 'Meta': {'ordering': "['-timestamp']", 'object_name': 'ImagePublishRecord'}, + 'change': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.ImageChange']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'publish_log'", 'to': "orm['catalogue.Image']"}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + 'catalogue.imagetag': { + 'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'ordering': ('django.db.models.fields.IntegerField', [], {}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}) + }, + 'catalogue.project': { + 'Meta': {'ordering': "['name']", 'object_name': 'Project'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'notes': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'cover.image': { + 'Meta': {'object_name': 'Image'}, + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'download_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'unique': 'True', 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.ImageField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'license_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'license_url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}), + 'source_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['catalogue'] \ No newline at end of file diff --git a/apps/catalogue/models/__init__.py b/apps/catalogue/models/__init__.py index bd069f1c..d0015c79 100755 --- a/apps/catalogue/models/__init__.py +++ b/apps/catalogue/models/__init__.py @@ -5,7 +5,9 @@ # from catalogue.models.project import Project from catalogue.models.chunk import Chunk -from catalogue.models.publish_log import BookPublishRecord, ChunkPublishRecord +from catalogue.models.image import Image +from catalogue.models.publish_log import (BookPublishRecord, + ChunkPublishRecord, ImagePublishRecord) from catalogue.models.book import Book from catalogue.models.listeners import * diff --git a/apps/catalogue/models/image.py b/apps/catalogue/models/image.py new file mode 100755 index 00000000..7b5ce1f2 --- /dev/null +++ b/apps/catalogue/models/image.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# +# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later. +# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. +# +from django.conf import settings +from django.contrib.sites.models import Site +from django.db import models +from django.template.loader import render_to_string +from django.utils.translation import ugettext_lazy as _ +from catalogue.helpers import cached_in_field +from catalogue.tasks import refresh_instance +from dvcs import models as dvcs_models + + +class Image(dvcs_models.Document): + """ An editable chunk of text. Every Book text is divided into chunks. """ + REPO_PATH = settings.CATALOGUE_IMAGE_REPO_PATH + + image = models.FileField(_('image'), upload_to='catalogue/images') + title = models.CharField(_('title'), max_length=255, blank=True) + slug = models.SlugField(_('slug'), unique=True) + public = models.BooleanField(_('public'), default=True, db_index=True) + + # cache + _short_html = models.TextField(null=True, blank=True, editable=False) + _new_publishable = models.NullBooleanField(editable=False) + _published = models.NullBooleanField(editable=False) + _changed = models.NullBooleanField(editable=False) + + class Meta: + app_label = 'catalogue' + ordering = ['title'] + verbose_name = _('image') + verbose_name_plural = _('images') + permissions = [('can_pubmark_image', 'Can mark images for publishing')] + + # Representing + # ============ + + def __unicode__(self): + return self.title + + @models.permalink + def get_absolute_url(self): + return ("catalogue_image", [self.slug]) + + def correct_about(self): + return "http://%s%s" % ( + Site.objects.get_current().domain, + self.get_absolute_url() + ) + + # State & cache + # ============= + + def last_published(self): + try: + return self.publish_log.all()[0].timestamp + except IndexError: + return None + + def assert_publishable(self): + from librarian.picture import WLPicture + from librarian import NoDublinCore, ParseError, ValidationError + + class SelfImageStore(object): + def path(self_, slug, mime_type): + """Returns own file object. Ignores slug ad mime_type.""" + return open(self.image.path) + + publishable = self.publishable() + assert publishable, _("There is no publishable revision") + picture_xml = publishable.materialize() + + try: + picture = WLPicture.from_string(picture_xml.encode('utf-8'), + image_store=SelfImageStore) + except ParseError, e: + raise AssertionError(_('Invalid XML') + ': ' + str(e)) + except NoDublinCore: + raise AssertionError(_('No Dublin Core found.')) + except ValidationError, e: + raise AssertionError(_('Invalid Dublin Core') + ': ' + str(e)) + + valid_about = self.correct_about() + assert picture.picture_info.about == valid_about, \ + _("rdf:about is not") + " " + valid_about + + def publishable_error(self): + try: + return self.assert_publishable() + except AssertionError, e: + return e + else: + return None + + def accessible(self, request): + return self.public or request.user.is_authenticated() + + def is_new_publishable(self): + change = self.publishable() + if not change: + return False + return not change.publish_log.exists() + new_publishable = cached_in_field('_new_publishable')(is_new_publishable) + + def is_published(self): + return self.publish_log.exists() + published = cached_in_field('_published')(is_published) + + def is_changed(self): + if self.head is None: + return False + return not self.head.publishable + changed = cached_in_field('_changed')(is_changed) + + @cached_in_field('_short_html') + def short_html(self): + return render_to_string( + 'catalogue/image_short.html', {'image': self}) + + def refresh(self): + """This should be done offline.""" + self.short_html + self.single + self.new_publishable + self.published + + def touch(self): + update = { + "_changed": self.is_changed(), + "_short_html": None, + "_new_publishable": self.is_new_publishable(), + "_published": self.is_published(), + } + Image.objects.filter(pk=self.pk).update(**update) + refresh_instance(self) + + def refresh(self): + """This should be done offline.""" + self.changed + self.short_html + + + # Publishing + # ========== + + def publish(self, user): + """Publishes the picture on behalf of a (local) user.""" + from base64 import b64encode + import apiclient + from catalogue.signals import post_publish + + self.assert_publishable() + change = self.publishable() + picture_xml = change.materialize() + picture_data = open(self.image.path).read() + apiclient.api_call(user, "pictures/", { + "picture_xml": picture_xml, + "picture_image_data": b64encode(picture_data), + }) + # record the publish + log = self.publish_log.create(user=user, change=change) + post_publish.send(sender=log) diff --git a/apps/catalogue/models/listeners.py b/apps/catalogue/models/listeners.py index 532f1e79..f98fba4d 100755 --- a/apps/catalogue/models/listeners.py +++ b/apps/catalogue/models/listeners.py @@ -5,7 +5,8 @@ # from django.contrib.auth.models import User from django.db import models -from catalogue.models import Book, Chunk +from catalogue.models import (Book, Chunk, Image, BookPublishRecord, + ImagePublishRecord) from catalogue.signals import post_publish from dvcs.signals import post_publishable @@ -23,6 +24,11 @@ def chunk_changed(sender, instance, created, **kwargs): models.signals.post_save.connect(chunk_changed, sender=Chunk) +def image_changed(sender, instance, created, **kwargs): + instance.touch() +models.signals.post_save.connect(image_changed, sender=Image) + + def user_changed(sender, instance, *args, **kwargs): books = set() for c in instance.chunk_set.all(): @@ -34,16 +40,24 @@ models.signals.post_save.connect(user_changed, sender=User) def publish_listener(sender, *args, **kwargs): - sender.book.touch() - for c in sender.book: - c.touch() + if isinstance(sender, BookPublishRecord): + sender.book.touch() + for c in sender.book: + c.touch() + elif isinstance(sender, ImagePublishRecord): + sender.image.touch() post_publish.connect(publish_listener) +def chunk_publishable_listener(sender, *args, **kwargs): + sender.tree.touch() + if isinstance(sender.tree, Chunk): + sender.tree.book.touch() +post_publishable.connect(chunk_publishable_listener) + def publishable_listener(sender, *args, **kwargs): sender.tree.touch() - sender.tree.book.touch() -post_publishable.connect(publishable_listener) +post_publishable.connect(publishable_listener, sender=Image) def listener_create(sender, instance, created, **kwargs): diff --git a/apps/catalogue/models/publish_log.py b/apps/catalogue/models/publish_log.py index f422e377..6cc86d08 100755 --- a/apps/catalogue/models/publish_log.py +++ b/apps/catalogue/models/publish_log.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import User from django.db import models from django.utils.translation import ugettext_lazy as _ -from catalogue.models import Chunk +from catalogue.models import Chunk, Image class BookPublishRecord(models.Model): @@ -37,3 +37,18 @@ class ChunkPublishRecord(models.Model): app_label = 'catalogue' verbose_name = _('chunk publish record') verbose_name = _('chunk publish records') + + +class ImagePublishRecord(models.Model): + """A record left after publishing an Image.""" + + image = models.ForeignKey(Image, verbose_name=_('image'), related_name='publish_log') + timestamp = models.DateTimeField(_('time'), auto_now_add=True) + user = models.ForeignKey(User, verbose_name=_('user')) + change = models.ForeignKey(Image.change_model, related_name='publish_log', verbose_name=_('change')) + + class Meta: + app_label = 'catalogue' + ordering = ['-timestamp'] + verbose_name = _('image publish record') + verbose_name = _('image publish records') diff --git a/apps/catalogue/templates/catalogue/activity.html b/apps/catalogue/templates/catalogue/activity.html index c88b5388..3bb8afb2 100755 --- a/apps/catalogue/templates/catalogue/activity.html +++ b/apps/catalogue/templates/catalogue/activity.html @@ -3,6 +3,9 @@ {% load wall %} +{% block titleextra %}{% trans "Activity" %}{% endblock %} + + {% block content %}

< diff --git a/apps/catalogue/templates/catalogue/base.html b/apps/catalogue/templates/catalogue/base.html index d2af462b..9a56d726 100644 --- a/apps/catalogue/templates/catalogue/base.html +++ b/apps/catalogue/templates/catalogue/base.html @@ -2,10 +2,11 @@ {% load catalogue %} - + {% compressed_css 'catalogue' %} - {% block title %}{% trans "Platforma Redakcyjna" %}{% endblock title %} + {% block title %}{% trans "Platforma Redakcyjna" %} :: + {% block titleextra %}{% endblock %}{% endblock title %} {% block add_css %}{% endblock %} @@ -13,7 +14,7 @@
- +
diff --git a/apps/catalogue/templates/catalogue/book_append_to.html b/apps/catalogue/templates/catalogue/book_append_to.html index 76a59621..c1ecc290 100755 --- a/apps/catalogue/templates/catalogue/book_append_to.html +++ b/apps/catalogue/templates/catalogue/book_append_to.html @@ -1,6 +1,8 @@ {% extends "catalogue/base.html" %} {% load i18n %} +{% block titleextra %}{% trans "Append book" %}{% endblock %} + {% block leftcolumn %}
{% csrf_token %} diff --git a/apps/catalogue/templates/catalogue/book_detail.html b/apps/catalogue/templates/catalogue/book_detail.html index eea6a6c7..4db8a9be 100755 --- a/apps/catalogue/templates/catalogue/book_detail.html +++ b/apps/catalogue/templates/catalogue/book_detail.html @@ -1,6 +1,10 @@ {% extends "catalogue/base.html" %} {% load book_list comments i18n %} + +{% block titleextra %}{{ book.title }}{% endblock %} + + {% block content %} diff --git a/apps/catalogue/templates/catalogue/book_edit.html b/apps/catalogue/templates/catalogue/book_edit.html index 3fffa963..43fe0ea9 100755 --- a/apps/catalogue/templates/catalogue/book_edit.html +++ b/apps/catalogue/templates/catalogue/book_edit.html @@ -1,6 +1,10 @@ {% extends "catalogue/base.html" %} {% load i18n %} + +{% block titleextra %}{% trans "Edit book" %}{% endblock %} + + {% block leftcolumn %} {% csrf_token %} diff --git a/apps/catalogue/templates/catalogue/book_html.html b/apps/catalogue/templates/catalogue/book_html.html new file mode 100755 index 00000000..af4cfa79 --- /dev/null +++ b/apps/catalogue/templates/catalogue/book_html.html @@ -0,0 +1,30 @@ +{% load i18n %} +{% load compressed %} + + + + + {{ book.title }} + + + +
+ {#% book_info book %#} +
+ + + {{ html|safe }} + + + diff --git a/apps/catalogue/templates/catalogue/book_list/book_list.html b/apps/catalogue/templates/catalogue/book_list/book_list.html index 90ae183f..8e254363 100755 --- a/apps/catalogue/templates/catalogue/book_list/book_list.html +++ b/apps/catalogue/templates/catalogue/book_list/book_list.html @@ -2,7 +2,7 @@ {% load pagination_tags %} - + {% if not viewed_user %} @@ -48,6 +48,8 @@ {% endif %}value="{{ user.username }}">{{ user.first_name }} {{ user.last_name }} ({{ user.count }}) {% endfor %} + {% else %} + {% endif %} + +{% if not viewed_user %} + +{% endif %} + +
+ + + + + + + + {% if not viewed_user %} + + {% endif %} + + + + + + {% with cnt=objects|length %} + {% autopaginate objects 100 %} + + {% for item in objects %} + {{ item.short_html|safe }} + {% endfor %} + + + {% endwith %} +
+
+ +
+
+ {% paginate %} + {% blocktrans count c=cnt %}{{c}} image{% plural %}{{c}} images{% endblocktrans %}
+{% if not objects %} +

{% trans "No images found." %}

+{% endif %} diff --git a/apps/catalogue/templates/catalogue/my_page.html b/apps/catalogue/templates/catalogue/my_page.html index fd4e84ef..c6b61ce8 100755 --- a/apps/catalogue/templates/catalogue/my_page.html +++ b/apps/catalogue/templates/catalogue/my_page.html @@ -12,6 +12,9 @@ {% compressed_css 'book_list' %} {% endblock %} +{% block titleextra %}{% trans "My page" %}{% endblock %} + + {% block leftcolumn %} {% book_list request.user %} {% endblock leftcolumn %} diff --git a/apps/catalogue/templates/catalogue/upload_pdf.html b/apps/catalogue/templates/catalogue/upload_pdf.html new file mode 100755 index 00000000..265b84ad --- /dev/null +++ b/apps/catalogue/templates/catalogue/upload_pdf.html @@ -0,0 +1,20 @@ +{% extends "catalogue/base.html" %} +{% load i18n %} + + +{% block titleextra %}{% trans "PDF file upload" %}{% endblock %} + + +{% block content %} + + +

{% trans "PDF file upload" %}

+ +
+{% csrf_token %} +{{ form.as_p }} +

+
+ + +{% endblock content %} diff --git a/apps/catalogue/templates/catalogue/user_list.html b/apps/catalogue/templates/catalogue/user_list.html index c4b1dfc4..26434053 100755 --- a/apps/catalogue/templates/catalogue/user_list.html +++ b/apps/catalogue/templates/catalogue/user_list.html @@ -2,6 +2,10 @@ {% load i18n %} + +{% block titleextra %}{% trans "Users" %}{% endblock %} + + {% block leftcolumn %}

{% trans "Users" %}

diff --git a/apps/catalogue/templates/catalogue/user_page.html b/apps/catalogue/templates/catalogue/user_page.html index 89b4ecef..4be4ca3e 100755 --- a/apps/catalogue/templates/catalogue/user_page.html +++ b/apps/catalogue/templates/catalogue/user_page.html @@ -4,6 +4,9 @@ {% load catalogue book_list wall %} +{% block titleextra %}{{ viewed_user|nice_name }}{% endblock %} + + {% block leftcolumn %}

{{ viewed_user|nice_name }}

{% book_list viewed_user %} diff --git a/apps/catalogue/templates/catalogue/wall.html b/apps/catalogue/templates/catalogue/wall.html index 25550fe6..d7f6c9e6 100755 --- a/apps/catalogue/templates/catalogue/wall.html +++ b/apps/catalogue/templates/catalogue/wall.html @@ -7,7 +7,8 @@
  • {% if item.get_email %} - {% gravatar_img_for_email item.get_email 32 %} + Avatar
    {% endif %}
    diff --git a/apps/catalogue/templatetags/book_list.py b/apps/catalogue/templatetags/book_list.py index 14149e84..1357c321 100755 --- a/apps/catalogue/templatetags/book_list.py +++ b/apps/catalogue/templatetags/book_list.py @@ -5,7 +5,7 @@ from django.db.models import Q, Count from django import template from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import User -from catalogue.models import Chunk, Project +from catalogue.models import Chunk, Image, Project register = template.Library() @@ -145,3 +145,58 @@ def book_list(context, user=None): }) return new_context + + + +_image_states = [ + ('publishable', _('publishable'), Q(_new_publishable=True)), + ('changed', _('changed'), Q(_changed=True)), + ('published', _('published'), Q(_published=True)), + ('unpublished', _('unpublished'), Q(_published=False)), + ('empty', _('empty'), Q(head=None)), + ] +_image_states_options = [s[:2] for s in _image_states] +_image_states_dict = dict([(s[0], s[2]) for s in _image_states]) + +def image_list_filter(request, **kwargs): + + def arg_or_GET(field): + return kwargs.get(field, request.GET.get(field)) + + images = Image.objects.all() + + if not request.user.is_authenticated(): + images = images.filter(public=True) + + state = arg_or_GET('status') + if state in _image_states_dict: + images = images.filter(_image_states_dict[state]) + + images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username') + images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug') + images = search_filter(images, arg_or_GET('title'), ['title', 'title']) + return images + + +@register.inclusion_tag('catalogue/image_table.html', takes_context=True) +def image_list(context, user=None): + request = context['request'] + + if user: + filters = {"user": user} + new_context = {"viewed_user": user} + else: + filters = {} + new_context = {"users": User.objects.annotate( + count=Count('image')).filter(count__gt=0).order_by( + '-count', 'last_name', 'first_name')} + + new_context.update({ + "filters": True, + "request": request, + "objects": image_list_filter(request, **filters), + "stages": Image.tag_model.objects.all(), + "states": _image_states_options, + }) + + return new_context diff --git a/apps/catalogue/templatetags/catalogue.py b/apps/catalogue/templatetags/catalogue.py index 8d5ff65b..07c5cf9d 100644 --- a/apps/catalogue/templatetags/catalogue.py +++ b/apps/catalogue/templatetags/catalogue.py @@ -28,6 +28,7 @@ def main_tabs(context): tabs.append(Tab('activity', _('Activity'), reverse("catalogue_activity"))) tabs.append(Tab('all', _('All'), reverse("catalogue_document_list"))) + tabs.append(Tab('images', _('Images'), reverse("catalogue_image_list"))) tabs.append(Tab('users', _('Users'), reverse("catalogue_users"))) if user.has_perm('catalogue.add_book'): diff --git a/apps/catalogue/urls.py b/apps/catalogue/urls.py index e72b88db..3627bf04 100644 --- a/apps/catalogue/urls.py +++ b/apps/catalogue/urls.py @@ -9,6 +9,11 @@ from catalogue.views import GalleryView urlpatterns = patterns('catalogue.views', url(r'^$', RedirectView.as_view(url='catalogue/')), + url(r'^images/$', 'image_list', name='catalogue_image_list'), + url(r'^image/(?P[^/]+)/$', 'image', name="catalogue_image"), + url(r'^image/(?P[^/]+)/publish$', 'publish_image', + name="catalogue_publish_image"), + url(r'^catalogue/$', 'document_list', name='catalogue_document_list'), url(r'^user/$', 'my', name='catalogue_user'), url(r'^user/(?P[^/]+)/$', 'user', name='catalogue_user'), @@ -26,7 +31,6 @@ urlpatterns = patterns('catalogue.views', 'create_missing', name='catalogue_create_missing'), url(r'^book/(?P[^/]+)/publish$', 'publish', name="catalogue_publish"), - #url(r'^(?P[^/]+)/publish/(?P\d+)$', 'publish', name="catalogue_publish"), url(r'^book/(?P[^/]+)/$', 'book', name="catalogue_book"), url(r'^book/(?P[^/]+)/gallery/$', diff --git a/apps/catalogue/views.py b/apps/catalogue/views.py index ebc35751..01e4d1fa 100644 --- a/apps/catalogue/views.py +++ b/apps/catalogue/views.py @@ -14,18 +14,18 @@ from django.db.models import Count, Q from django.db import transaction from django import http from django.http import Http404, HttpResponse, HttpResponseForbidden -from django.shortcuts import get_object_or_404, render, render_to_response +from django.shortcuts import get_object_or_404, render from django.utils.encoding import iri_to_uri from django.utils.http import urlquote_plus from django.utils.translation import ugettext_lazy as _ from django.views.decorators.http import require_POST -from django.template import RequestContext from apiclient import NotAuthorizedError from catalogue import forms from catalogue import helpers from catalogue.helpers import active_tab -from catalogue.models import Book, Chunk, BookPublishRecord, ChunkPublishRecord, Project +from catalogue.models import (Book, Chunk, Image, BookPublishRecord, + ChunkPublishRecord, ImagePublishRecord, Project) from fileupload.views import UploadView # @@ -42,6 +42,12 @@ def document_list(request): return render(request, 'catalogue/document_list.html') +@active_tab('images') +@never_cache +def image_list(request, user=None): + return render(request, 'catalogue/image_list.html') + + @never_cache def user(request, username): user = get_object_or_404(User, username=username) @@ -239,8 +245,7 @@ def book_html(request, slug): # book_themes = book_themes.items() # book_themes.sort(key=lambda s: s[0].sort_key) - return render_to_response('catalogue/book_text.html', locals(), - context_instance=RequestContext(request)) + return render(request, 'catalogue/book_text.html', locals()) @never_cache @@ -315,6 +320,36 @@ def book(request, slug): }) +def image(request, slug): + image = get_object_or_404(Image, slug=slug) + if not image.accessible(request): + return HttpResponseForbidden("Not authorized.") + + if request.user.has_perm('catalogue.change_image'): + if request.method == "POST": + form = forms.ImageForm(request.POST, instance=image) + if form.is_valid(): + form.save() + return http.HttpResponseRedirect(image.get_absolute_url()) + else: + form = forms.ImageForm(instance=image) + editable = True + else: + form = forms.ReadonlyImageForm(instance=image) + editable = False + + publish_error = image.publishable_error() + publishable = publish_error is None + + return render(request, "catalogue/image_detail.html", { + "object": image, + "publishable": publishable, + "publishable_error": publish_error, + "form": form, + "editable": editable, + }) + + @permission_required('catalogue.add_chunk') def chunk_add(request, slug, chunk): try: @@ -488,6 +523,23 @@ def publish(request, slug): return http.HttpResponseRedirect(book.get_absolute_url()) +@require_POST +@login_required +def publish_image(request, slug): + image = get_object_or_404(Image, slug=slug) + if not image.accessible(request): + return HttpResponseForbidden("Not authorized.") + + try: + image.publish(request.user) + except NotAuthorizedError: + return http.HttpResponseRedirect(reverse('apiclient_oauth')) + except BaseException, e: + return http.HttpResponse(e) + else: + return http.HttpResponseRedirect(image.get_absolute_url()) + + class GalleryView(UploadView): def get_object(self, request, slug): book = get_object_or_404(Book, slug=slug) diff --git a/apps/dvcs/locale/pl/LC_MESSAGES/django.mo b/apps/dvcs/locale/pl/LC_MESSAGES/django.mo index 4c3a1ffc..dfd85c25 100644 Binary files a/apps/dvcs/locale/pl/LC_MESSAGES/django.mo and b/apps/dvcs/locale/pl/LC_MESSAGES/django.mo differ diff --git a/apps/dvcs/locale/pl/LC_MESSAGES/django.po b/apps/dvcs/locale/pl/LC_MESSAGES/django.po index 64ddfd78..c0365d56 100644 --- a/apps/dvcs/locale/pl/LC_MESSAGES/django.po +++ b/apps/dvcs/locale/pl/LC_MESSAGES/django.po @@ -7,16 +7,15 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2011-10-03 15:35+0200\n" -"PO-Revision-Date: 2011-10-03 15:35+0100\n" +"POT-Creation-Date: 2011-12-14 15:25+0100\n" +"PO-Revision-Date: 2011-12-14 15: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" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n" #: models.py:19 msgid "name" @@ -30,86 +29,96 @@ msgstr "slug" msgid "ordering" msgstr "kolejność" -#: models.py:29 -msgid "tag" -msgstr "tag" - -#: models.py:30 models.py:196 -msgid "tags" -msgstr "tagi" - -#: models.py:72 +#: models.py:70 msgid "author" msgstr "autor" -#: models.py:73 +#: models.py:71 msgid "author name" msgstr "imię i nazwisko autora" -#: models.py:75 models.py:79 +#: models.py:73 +#: models.py:77 msgid "Used if author is not set." msgstr "Używane, gdy nie jest ustawiony autor." -#: models.py:77 +#: models.py:75 msgid "author email" msgstr "e-mail autora" -#: models.py:81 +#: models.py:79 msgid "revision" msgstr "rewizja" -#: models.py:85 +#: models.py:83 msgid "parent" msgstr "rodzic" -#: models.py:90 +#: models.py:88 msgid "merge parent" msgstr "drugi rodzic" -#: models.py:93 +#: models.py:91 msgid "description" msgstr "opis" -#: models.py:96 +#: models.py:94 msgid "publishable" msgstr "do publikacji" -#: models.py:102 +#: models.py:176 +msgid "tag" +msgstr "tag" + +#: models.py:176 +#: models.py:178 +#: models.py:194 +#: models.py:196 +msgid "for:" +msgstr "dla:" + +#: models.py:178 +#: models.py:202 +msgid "tags" +msgstr "tagi" + +#: models.py:194 msgid "change" msgstr "zmiana" -#: models.py:103 +#: models.py:196 msgid "changes" msgstr "zmiany" -#: models.py:195 +#: models.py:201 msgid "document" msgstr "dokument" -#: models.py:197 +#: models.py:203 msgid "data" msgstr "dane" -#: models.py:211 +#: models.py:217 msgid "stage" msgstr "etap" -#: models.py:219 +#: models.py:225 msgid "head" msgstr "głowica" -#: models.py:220 +#: models.py:226 msgid "This document's current head." msgstr "Aktualna wersja dokumentu." -#: models.py:224 +#: models.py:230 msgid "creator" msgstr "utworzył" -#: models.py:239 +#: models.py:245 msgid "user" msgstr "użytkownik" -#: models.py:239 +#: models.py:245 msgid "Work assignment." msgstr "Przypisanie pracy użytkownikowi." + diff --git a/apps/dvcs/models.py b/apps/dvcs/models.py index cf8d75dc..ec647946 100644 --- a/apps/dvcs/models.py +++ b/apps/dvcs/models.py @@ -6,7 +6,7 @@ from django.core.files.base import ContentFile from django.core.files.storage import FileSystemStorage from django.db import models, transaction from django.db.models.base import ModelBase -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import string_concat, ugettext_lazy as _ from mercurial import simplemerge from django.conf import settings @@ -26,8 +26,6 @@ class Tag(models.Model): class Meta: abstract = True ordering = ['ordering'] - verbose_name = _("tag") - verbose_name_plural = _("tags") def __unicode__(self): return self.name @@ -99,8 +97,6 @@ class Change(models.Model): abstract = True ordering = ('created_at',) unique_together = ['tree', 'revision'] - verbose_name = _("change") - verbose_name_plural = _("changes") def __unicode__(self): return u"Id: %r, Tree %r, Parent %r, Data: %s" % (self.id, self.tree_id, self.parent_id, self.data) @@ -175,6 +171,10 @@ def create_tag_model(model): class Meta(Tag.Meta): app_label = model._meta.app_label + verbose_name = string_concat(_("tag"), " ", _("for:"), " ", + model._meta.verbose_name) + verbose_name_plural = string_concat(_("tags"), " ", _("for:"), " ", + model._meta.verbose_name) attrs = { '__module__': model.__module__, @@ -189,6 +189,10 @@ def create_change_model(model): class Meta(Change.Meta): app_label = model._meta.app_label + verbose_name = string_concat(_("change"), " ", _("for:"), " ", + model._meta.verbose_name) + verbose_name_plural = string_concat(_("changes"), " ", _("for:"), " ", + model._meta.verbose_name) attrs = { '__module__': model.__module__, diff --git a/apps/wiki/templates/wiki/document_details_base.html b/apps/wiki/templates/wiki/document_details_base.html index e164a9ee..891e9e85 100644 --- a/apps/wiki/templates/wiki/document_details_base.html +++ b/apps/wiki/templates/wiki/document_details_base.html @@ -1,14 +1,14 @@ {% extends "base.html" %} {% load toolbar_tags i18n %} -{% block title %}{{ book.title }} - {{ block.super }}{% endblock %} +{% block titleextra %}{{ chunk.pretty_title }}{% endblock %} {% block extrahead %} {% load compressed %} {% compressed_css 'detail' %} {% endblock %} {% block extrabody %} - {% compressed_js 'detail' %} @@ -27,7 +27,7 @@