Add projects for images and port mass_edit.
authorRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 27 Mar 2014 12:20:29 +0000 (13:20 +0100)
committerRadek Czajka <radekczajka@nowoczesnapolska.org.pl>
Thu, 27 Mar 2014 12:20:29 +0000 (13:20 +0100)
Also: remove the ability to mass set things as publishable,
page the user list in mass-edit so that it fits vertically,
include image actions on wall,
show username for users without first and last names.

20 files changed:
apps/catalogue/forms.py
apps/catalogue/locale/pl/LC_MESSAGES/django.mo
apps/catalogue/locale/pl/LC_MESSAGES/django.po
apps/catalogue/migrations/0013_auto__add_field_image_project.py [new file with mode: 0644]
apps/catalogue/models/image.py
apps/catalogue/templates/catalogue/book_list/book.html
apps/catalogue/templates/catalogue/book_list/book_list.html
apps/catalogue/templates/catalogue/book_list/chunk.html
apps/catalogue/templates/catalogue/image_list.html
apps/catalogue/templates/catalogue/image_short.html
apps/catalogue/templates/catalogue/image_table.html
apps/catalogue/templates/catalogue/user_list.html
apps/catalogue/templates/catalogue/wall.html
apps/catalogue/templatetags/book_list.py
apps/catalogue/templatetags/common_tags.py
apps/catalogue/templatetags/wall.py
apps/catalogue/urls.py
apps/catalogue/views.py
redakcja/static/js/catalogue/book_list.js
redakcja/static/js/wiki_img/wikiapi.js

index 83b1652..a2ae86a 100644 (file)
@@ -175,7 +175,7 @@ class ImageForm(forms.ModelForm):
 
 
 class ReadonlyImageForm(ImageForm):
-    """Form used for not editing a Book."""
+    """Form used for not editing an Image."""
 
     def __init__(self, *args, **kwargs):
         ret = super(ReadonlyImageForm, self).__init__(*args, **kwargs)
index 64f78b4..17689b8 100644 (file)
Binary files a/apps/catalogue/locale/pl/LC_MESSAGES/django.mo and b/apps/catalogue/locale/pl/LC_MESSAGES/django.mo differ
index d98440f..64ddd9f 100644 (file)
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Platforma Redakcyjna\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-03-26 16:13+0100\n"
-"PO-Revision-Date: 2014-03-26 16:14+0100\n"
+"POT-Creation-Date: 2014-03-27 13:16+0100\n"
+"PO-Revision-Date: 2014-03-27 13:17+0100\n"
 "Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
 "Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org."
 "pl>\n"
@@ -61,23 +61,23 @@ msgstr "Dokument o tym slugu już istnieje w repozytorium."
 msgid "File should be UTF-8 encoded."
 msgstr "Plik powinien mieć kodowanie UTF-8."
 
-#: views.py:552 models/book.py:56
+#: views.py:576 models/book.py:56
 msgid "books"
 msgstr "książki"
 
-#: views.py:554
+#: views.py:578
 msgid "scan gallery"
 msgstr "galeria skanów"
 
-#: models/book.py:28 models/chunk.py:23 models/image.py:21
+#: models/book.py:28 models/chunk.py:23 models/image.py:22
 msgid "title"
 msgstr "tytuł"
 
-#: models/book.py:29 models/chunk.py:24 models/image.py:22
+#: models/book.py:29 models/chunk.py:24 models/image.py:23
 msgid "slug"
 msgstr "slug"
 
-#: models/book.py:30 models/image.py:23
+#: models/book.py:30 models/image.py:24
 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/image.py:80
+#: models/book.py:266 models/image.py:82
 msgid "Invalid XML"
 msgstr "Nieprawidłowy XML"
 
-#: models/book.py:268 models/image.py:82
+#: models/book.py:268 models/image.py:84
 msgid "No Dublin Core found."
 msgstr "Brak sekcji Dublin Core."
 
-#: models/book.py:270 models/image.py:84
+#: models/book.py:270 models/image.py:86
 msgid "Invalid Dublin Core"
 msgstr "Nieprawidłowy Dublin Core"
 
-#: models/book.py:273 models/image.py:88
+#: models/book.py:273 models/image.py:90
 msgid "rdf:about is not"
 msgstr "rdf:about jest różny od"
 
@@ -137,15 +137,15 @@ msgstr "część"
 msgid "chunks"
 msgstr "części"
 
-#: models/image.py:20 models/image.py:34 models/publish_log.py:45
+#: models/image.py:21 models/image.py:36 models/publish_log.py:45
 msgid "image"
 msgstr "obraz"
 
-#: models/image.py:35
+#: models/image.py:37
 msgid "images"
 msgstr "obrazy"
 
-#: models/image.py:73
+#: models/image.py:75
 msgid "There is no publishable revision"
 msgstr "Żadna wersja nie została oznaczona do publikacji."
 
@@ -157,7 +157,8 @@ msgstr "nazwa"
 msgid "notes"
 msgstr "notatki"
 
-#: models/project.py:19 templates/catalogue/book_list/book_list.html:64
+#: models/project.py:19 templates/catalogue/image_table.html:58
+#: templates/catalogue/book_list/book_list.html:65
 msgid "project"
 msgstr "projekt"
 
@@ -170,7 +171,7 @@ msgid "time"
 msgstr "czas"
 
 #: models/publish_log.py:19 models/publish_log.py:47
-#: templates/catalogue/wall.html:19
+#: templates/catalogue/wall.html:20
 msgid "user"
 msgstr "użytkownik"
 
@@ -236,7 +237,8 @@ msgid "Chunks"
 msgstr "Części"
 
 #: templates/catalogue/book_detail.html:49
-#: templates/catalogue/image_detail.html:36 templatetags/wall.py:78
+#: templates/catalogue/image_detail.html:36 templatetags/wall.py:108
+#: templatetags/wall.py:129
 msgid "Publication"
 msgstr "Publikacja"
 
@@ -319,8 +321,8 @@ msgid "Add chunk"
 msgstr "Dodaj część"
 
 #: 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
+#: templates/catalogue/book_list/book.html:9
+#: templates/catalogue/book_list/chunk.html:7
 msgid "Chunk settings"
 msgstr "Ustawienia części"
 
@@ -399,43 +401,44 @@ msgstr "Edytor"
 msgid "Proceed to the editor."
 msgstr "Przejdź do edytora."
 
-#: templates/catalogue/image_list.html:7
+#: templates/catalogue/image_list.html:8
 msgid "Image list"
 msgstr "Lista obrazów"
 
-#: templates/catalogue/image_short.html:4
+#: templates/catalogue/image_short.html:6
 msgid "Image settings"
 msgstr "Ustawienia obrazu"
 
-#: templates/catalogue/image_table.html:19
-#: templates/catalogue/book_list/book_list.html:27
+#: templates/catalogue/image_table.html:23
+#: templates/catalogue/book_list/book_list.html:28
 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
+#: templates/catalogue/image_table.html:28
+#: templates/catalogue/book_list/book_list.html:33
 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:66
+#: templates/catalogue/image_table.html:30
+#: templates/catalogue/image_table.html:41
+#: templates/catalogue/image_table.html:60
+#: templates/catalogue/book_list/book_list.html:35
+#: templates/catalogue/book_list/book_list.html:46
+#: templates/catalogue/book_list/book_list.html:67
 msgid "none"
 msgstr "brak"
 
-#: templates/catalogue/image_table.html:35
-#: templates/catalogue/book_list/book_list.html:43
+#: templates/catalogue/image_table.html:39
+#: templates/catalogue/book_list/book_list.html:44
 msgid "editor"
 msgstr "redaktor"
 
-#: templates/catalogue/image_table.html:46
-#: templates/catalogue/book_list/book_list.html:56
+#: templates/catalogue/image_table.html:50
+#: templates/catalogue/book_list/book_list.html:57
 msgid "status"
 msgstr "status"
 
-#: templates/catalogue/image_table.html:63
+#: templates/catalogue/image_table.html:77
 #, python-format
 msgid "%(c)s image"
 msgid_plural "%(c)s images"
@@ -443,10 +446,30 @@ msgstr[0] "%(c)s obraz"
 msgstr[1] "%(c)s obrazy"
 msgstr[2] "%(c)s obrazów"
 
-#: templates/catalogue/image_table.html:68
+#: templates/catalogue/image_table.html:82
 msgid "No images found."
 msgstr "Nie znaleziono obrazów."
 
+#: templates/catalogue/image_table.html:88
+#: templates/catalogue/book_list/book_list.html:102
+msgid "Set stage"
+msgstr "Ustaw etap"
+
+#: templates/catalogue/image_table.html:89
+#: templates/catalogue/book_list/book_list.html:103
+msgid "Set user"
+msgstr "Przypisz redaktora"
+
+#: templates/catalogue/image_table.html:91
+#: templates/catalogue/book_list/book_list.html:105
+msgid "Project"
+msgstr "Projekt"
+
+#: templates/catalogue/image_table.html:92
+#: templates/catalogue/book_list/book_list.html:106
+msgid "More users"
+msgstr "Więcej użytkowników"
+
 #: templates/catalogue/my_page.html:15 templatetags/catalogue.py:27
 msgid "My page"
 msgstr "Moja strona"
@@ -464,29 +487,29 @@ msgstr "Ostatnia aktywność dla:"
 msgid "PDF file upload"
 msgstr "Ładowanie pliku PDF"
 
-#: templates/catalogue/user_list.html:6 templates/catalogue/user_list.html:11
+#: templates/catalogue/user_list.html:7 templates/catalogue/user_list.html:12
 #: templatetags/catalogue.py:32
 msgid "Users"
 msgstr "Użytkownicy"
 
-#: templates/catalogue/wall.html:29
+#: templates/catalogue/wall.html:30
 msgid "not logged in"
 msgstr "nie zalogowany"
 
-#: templates/catalogue/wall.html:34
+#: templates/catalogue/wall.html:35
 msgid "No activity recorded."
 msgstr "Nie zanotowano aktywności."
 
-#: templates/catalogue/book_list/book.html:7
-#: templates/catalogue/book_list/book.html:28
+#: templates/catalogue/book_list/book.html:8
+#: templates/catalogue/book_list/book.html:29
 msgid "Book settings"
 msgstr "Ustawienia książki"
 
-#: templates/catalogue/book_list/book_list.html:22
+#: templates/catalogue/book_list/book_list.html:23
 msgid "Show hidden books"
 msgstr "Pokaż ukryte książki"
 
-#: templates/catalogue/book_list/book_list.html:90
+#: templates/catalogue/book_list/book_list.html:91
 #, python-format
 msgid "%(c)s book"
 msgid_plural "%(c)s books"
@@ -494,34 +517,10 @@ 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:95
+#: templates/catalogue/book_list/book_list.html:96
 msgid "No books found."
 msgstr "Nie znaleziono książek."
 
-#: templates/catalogue/book_list/book_list.html:101
-msgid "Set stage"
-msgstr "Ustaw etap"
-
-#: templates/catalogue/book_list/book_list.html:102
-msgid "Set user"
-msgstr "Przypisz redaktora"
-
-#: templates/catalogue/book_list/book_list.html:104
-msgid "Project"
-msgstr "Projekt"
-
-#: templates/catalogue/book_list/book_list.html:105
-msgid "Mark publishable"
-msgstr "Oznacz do publikacji"
-
-#: templates/catalogue/book_list/book_list.html:106
-msgid "Mark not publishable"
-msgstr "Odznacz do publikacji"
-
-#: templates/catalogue/book_list/book_list.html:107
-msgid "Other user"
-msgstr "Inny użytkownik"
-
 #: templatetags/book_list.py:84 templatetags/book_list.py:152
 msgid "publishable"
 msgstr "do publikacji"
@@ -558,18 +557,27 @@ msgstr "Dodaj"
 msgid "Covers"
 msgstr "Okładki"
 
-#: templatetags/wall.py:49
+#: templatetags/wall.py:49 templatetags/wall.py:78
 msgid "Related edit"
 msgstr "Powiązana zmiana"
 
-#: templatetags/wall.py:51
+#: templatetags/wall.py:51 templatetags/wall.py:80
 msgid "Edit"
 msgstr "Zmiana"
 
-#: templatetags/wall.py:99
+#: templatetags/wall.py:150
 msgid "Comment"
 msgstr "Komentarz"
 
+#~ msgid "Mark publishable"
+#~ msgstr "Oznacz do publikacji"
+
+#~ msgid "Mark not publishable"
+#~ msgstr "Odznacz do publikacji"
+
+#~ msgid "Other user"
+#~ msgstr "Inny użytkownik"
+
 #~ msgid "Admin"
 #~ msgstr "Administracja"
 
diff --git a/apps/catalogue/migrations/0013_auto__add_field_image_project.py b/apps/catalogue/migrations/0013_auto__add_field_image_project.py
new file mode 100644 (file)
index 0000000..6ae3564
--- /dev/null
@@ -0,0 +1,196 @@
+# -*- 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 field 'Image.project'
+        db.add_column(u'catalogue_image', 'project',
+                      self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.Project'], null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Image.project'
+        db.delete_column(u'catalogue_image', 'project_id')
+
+
+    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'}),
+            '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': '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
index 7b5ce1f..fffa4b3 100755 (executable)
@@ -9,6 +9,7 @@ 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.models import Project
 from catalogue.tasks import refresh_instance
 from dvcs import models as dvcs_models
 
@@ -21,6 +22,7 @@ class Image(dvcs_models.Document):
     title = models.CharField(_('title'), max_length=255, blank=True)
     slug = models.SlugField(_('slug'), unique=True)
     public = models.BooleanField(_('public'), default=True, db_index=True)
+    project = models.ForeignKey(Project, null=True, blank=True)
 
     # cache
     _short_html = models.TextField(null=True, blank=True, editable=False)
index 5866e6a..f6a0fcd 100755 (executable)
@@ -1,4 +1,5 @@
 {% load i18n %}
+{% load username from common_tags %}
 
 {% if book.single %}
     {% with book.0 as chunk %}
@@ -13,7 +14,7 @@
             {{ chunk.stage }}
         {% else %}–
         {% endif %}</td>
-        <td class='user-column'>{% if chunk.user %}<a href="{% url 'catalogue_user' chunk.user.username %}">{{ chunk.user.first_name }} {{ chunk.user.last_name }}</a>{% endif %}</td>
+        <td class='user-column'>{% if chunk.user %}<a href="{% url 'catalogue_user' chunk.user.username %}">{{ chunk.user|username }}</a>{% endif %}</td>
         <td>
             {% if chunk.published %}P{% endif %}
             {% if book.new_publishable %}p{% endif %}
index 8e25436..e238827 100755 (executable)
@@ -1,5 +1,6 @@
 {% load i18n %}
 {% load pagination_tags %}
+{% load username from common_tags %}
 
 
 <form name='filter' action='{{ request.path }}'>
@@ -45,7 +46,7 @@
                         {% endif %}value="-">- {% trans "none" %} -</option>
                 {% for user in users %}
                     <option {% if request.GET.user == user.username %}selected="selected"
-                        {% endif %}value="{{ user.username }}">{{ user.first_name }} {{ user.last_name }} ({{ user.count }})</option>
+                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
                 {% endfor %}
             </select></th>
         {% else %}
 {% csrf_token %}
 <input type="hidden" name="ids" />
 <label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
-<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_stage" />
+<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_user" />
 <input type="hidden" name="status" />
 <label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
-<label for="mass_edit_publish">{% trans "Mark publishable" %}</label>
-<label for="mass_edit_unpublish">{% trans "Mark not publishable" %}</label>
-<label for="mass_edit_other">{% trans "Other user" %}</label>
+<label for="mass_edit_more_users">{% trans "More users" %}</label>
 </form>
 
 <select name="other-user" style="display:none;">
   {% for user in other_users %}
   <option {% if request.GET.user == user.username %}selected="selected"
-          {% endif %}value="{{ user.username }}">{{ user.first_name }} {{ user.last_name }} ({{ user.count }})</option>
+          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
   {% endfor %}
 </select>
index 7bdf3aa..0d21895 100755 (executable)
@@ -1,4 +1,5 @@
 {% load i18n %}
+{% load username from common_tags %}
 
 <tr>
     <td><input type="checkbox" name="select_chunk" value="{{chunk.id}}" data-book-id="{{chunk.book.id}}" /></td>
@@ -14,7 +15,7 @@
         {% endif %}</td>
         <td class='user-column'>{% if chunk.user %}
             <a href="{% url 'catalogue_user' chunk.user.username %}">
-                {{ chunk.user.first_name }} {{ chunk.user.last_name }}
+                {{ chunk.user|username }}
             </a>{% else %}
             
             {% endif %}</td>
index c6916e8..a5e35f7 100755 (executable)
@@ -2,11 +2,20 @@
 
 {% load i18n %}
 {% load catalogue book_list %}
+{% load compressed %}
 
 
 {% block titleextra %}{% trans "Image list" %}{% endblock %}
 
 
+{% block add_js %}
+{% compressed_js 'book_list' %}
+{% endblock %}
+
+{% block add_css %}
+{% compressed_css 'book_list' %}
+{% endblock %}
+
 
 {% block content %}
     {% image_list %}
index e64733b..c7ff77b 100755 (executable)
@@ -1,6 +1,8 @@
 {% load i18n %}
+{% load username from common_tags %}
 
 <tr>
+    <td><input type="checkbox" name="select_chunk" value="{{image.id}}"/></td>
     <td><a href="{% url 'catalogue_image' image.slug %}" title='{% trans "Image settings" %}'>[B]</a></td>
     <td><a target="_blank"
                 href="{% url 'wiki_img_editor' image.slug %}">
         {{ image.stage }}
     {% else %}–
     {% endif %}</td>
-    <td class='user-column'>{% if image.user %}<a href="{% url 'catalogue_user' image.user.username %}">{{ image.user.first_name }} {{ image.user.last_name }}</a>{% endif %}</td>
+    <td class='user-column'>{% if image.user %}<a href="{% url 'catalogue_user' image.user.username %}">{{ image.user|username }}</a>{% endif %}</td>
     <td>
         {% if image.published %}P{% endif %}
         {% if image.new_publishable %}p{% endif %}
         {% if image.changed %}+{% endif %}
     </td>
+    <td>{{ image.project.name }}</td>
 </tr>
index b152946..e6caedd 100755 (executable)
@@ -1,5 +1,7 @@
 {% load i18n %}
 {% load pagination_tags %}
+{% load username from common_tags %}
+
 
 
 <form name='filter' action='{{ request.path }}'>
     <input type='hidden' name="user" value="{{ request.GET.user }}" />
 {% endif %}
 <input type='hidden' name="status" value="{{ request.GET.status }}" />
+<input type='hidden' name="project" value="{{ request.GET.project }}" />
 </form>
 
 <table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
     <thead><tr>
+        <th></th>
         <th></th>
         <th class='book-search-column'>
             <form>
@@ -37,7 +41,7 @@
                         {% endif %}value="-">- {% trans "none" %} -</option>
                 {% for user in users %}
                     <option {% if request.GET.user == user.username %}selected="selected"
-                        {% endif %}value="{{ user.username }}">{{ user.first_name }} {{ user.last_name }} ({{ user.count }})</option>
+                        {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
                 {% endfor %}
             </select></th>
         {% endif %}
             {% endfor %}
         </select></th>
 
+        <th><select name="project" class="filter">
+            <option value=''>- {% trans "project" %} -</option>
+                <option {% if request.GET.project == '-' %}selected="selected"
+                        {% endif %}value="-">- {% trans "none" %} -</option>
+            {% for project in projects %}
+                <option {% if request.GET.project == project.pk|slugify %}selected="selected"
+                        {% endif %}value='{{ project.pk }}'>{{ project.name }}</option>
+            {% endfor %}
+        </select></th>
+
     </tr></thead>
 
     {% with cnt=objects|length %}
@@ -58,7 +72,7 @@
     {% for item in objects %}
         {{ item.short_html|safe }}
     {% endfor %}
-    <tr><th class='paginator' colspan="5">
+    <tr><th class='paginator' colspan="6">
         {% paginate %}
         {% blocktrans count c=cnt %}{{c}} image{% plural %}{{c}} images{% endblocktrans %}</th></tr>
     </tbody>
 {% if not objects %}
     <p>{% trans "No images found." %}</p>
 {% endif %}
+
+<form id='chunk_mass_edit' action='{% url "catalogue_image_mass_edit" %}' style="display:none;">
+{% csrf_token %}
+<input type="hidden" name="ids" />
+<label for="mass_edit_stage">{% trans "Set stage" %}</label><input type="hidden" name="stage" id="mass_edit_stage"/>
+<label for="mass_edit_user">{% trans "Set user" %}</label><input type="hidden" name="user" id="mass_edit_stage" />
+<input type="hidden" name="status" />
+<label for="mass_edit_project">{% trans "Project" %}</label><input type="hidden" name="project" id="mass_edit_project" />
+<label for="mass_edit_more_users">{% trans "More users" %}</label>
+</form>
+
+<select name="other-user" style="display:none;">
+  {% for user in other_users %}
+  <option {% if request.GET.user == user.username %}selected="selected"
+          {% endif %}value="{{ user.username }}">{{ user|username }} ({{ user.count }})</option>
+  {% endfor %}
+</select>
index 2643405..460a38b 100755 (executable)
@@ -1,6 +1,7 @@
 {% extends "catalogue/base.html" %}
 
 {% load i18n %}
+{% load username from common_tags %}
 
 
 {% block titleextra %}{% trans "Users" %}{% endblock %}
@@ -14,7 +15,7 @@
 {% for user in users %}
     <li><a href="{% url 'catalogue_user' user.username %}">
         <span class="chunkno">{{ forloop.counter }}.</span>
-        {{ user.first_name }} {{ user.last_name }}</a>
+        {{ user|username }}</a>
         ({{ user.count }})</li>
 {% endfor %}
 </ul>
index d7f6c9e..6483079 100755 (executable)
@@ -1,6 +1,7 @@
 {% load i18n %}
 {% load gravatar %}
 {% load email %}
+{% load username from common_tags %}
 
 <ul class='wall'>
 {% for item in wall %}
@@ -19,7 +20,7 @@
         <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|username }}</a>
             &lt;{{ item.user.email|email_link }}>
         {% else %}
             {{ item.user_name }}
index 1357c32..9ac996b 100755 (executable)
@@ -175,6 +175,7 @@ def image_list_filter(request, **kwargs):
     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'])
+    images = foreign_filter(images, arg_or_GET('project'), 'project', Project, 'pk')
     return images
 
 
@@ -187,9 +188,14 @@ def image_list(context, user=None):
         new_context = {"viewed_user": user}
     else:
         filters = {}
-        new_context = {"users": User.objects.annotate(
+        new_context = {
+            "users": User.objects.annotate(
                 count=Count('image')).filter(count__gt=0).order_by(
-                '-count', 'last_name', 'first_name')}
+                '-count', 'last_name', 'first_name'),
+            "other_users": User.objects.annotate(
+                count=Count('image')).filter(count=0).order_by(
+                'last_name', 'first_name'),
+                }
 
     new_context.update({
         "filters": True,
@@ -197,6 +203,7 @@ def image_list(context, user=None):
         "objects": image_list_filter(request, **filters),
         "stages": Image.tag_model.objects.all(),
         "states": _image_states_options,
+        "projects": Project.objects.all(),
     })
 
     return new_context
index ccaf03b..6baf4e5 100755 (executable)
@@ -5,3 +5,7 @@ register = template.Library()
 @register.filter
 def build_absolute_uri(uri, request):
     return request.build_absolute_uri(uri)
+
+@register.filter
+def username(user):
+    return ("%s %s" % (user.first_name, user.last_name)).lstrip() or user.username
index 28671fb..e12a313 100755 (executable)
@@ -7,7 +7,7 @@ from django.contrib.comments.models import Comment
 from django import template
 from django.utils.translation import ugettext as _
 
-from catalogue.models import Chunk, BookPublishRecord
+from catalogue.models import Chunk, BookPublishRecord, Image, ImagePublishRecord
 
 register = template.Library()
 
@@ -60,6 +60,36 @@ def changes_wall(user=None, max_len=None, day=None):
         yield w
 
 
+def image_changes_wall(user=None, max_len=None, day=None):
+    qs = Image.change_model.objects.order_by('-created_at')
+    qs = qs.select_related('author', 'tree', 'tree__title')
+    if user is not None:
+        qs = qs.filter(Q(author=user) | Q(tree__user=user))
+    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'
+        image = item.tree
+        w  = WallItem(tag)
+        if user and item.author != user:
+            w.header = _('Related edit')
+        else:
+            w.header = _('Edit')
+        w.title = image.title
+        w.summary = item.description
+        w.url = reverse('wiki_img_editor', 
+                args=[image.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
 
 
@@ -84,6 +114,27 @@ def published_wall(user=None, max_len=None, day=None):
         yield w
 
 
+def image_published_wall(user=None, max_len=None, day=None):
+    qs = ImagePublishRecord.objects.select_related('image__title')
+    if user:
+        # TODO: published my book
+        qs = qs.filter(Q(user=user))
+    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')
+        w.title = item.book.title
+        w.timestamp = item.timestamp
+        w.url = item.book.get_absolute_url()
+        w.user = item.user
+        w.email = item.user.email
+        yield w
+
+
 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:
@@ -140,6 +191,8 @@ def wall(context, user=None, max_len=100):
         "wall": big_wall([
             changes_wall(user, max_len),
             published_wall(user, max_len),
+            image_changes_wall(user, max_len),
+            image_published_wall(user, max_len),
             comments_wall(user, max_len),
         ], max_len)}
 
@@ -151,5 +204,7 @@ def day_wall(context, day):
         "wall": big_wall([
             changes_wall(day=day),
             published_wall(day=day),
+            image_changes_wall(day=day),
+            image_published_wall(day=day),
             comments_wall(day=day),
         ])}
index 3627bf0..908144f 100644 (file)
@@ -50,6 +50,8 @@ urlpatterns = patterns('catalogue.views',
         'book_append', name="catalogue_book_append"),
     url(r'^chunk_mass_edit',
         'chunk_mass_edit', name='catalogue_chunk_mass_edit'),
+    url(r'^image_mass_edit',
+        'image_mass_edit', name='catalogue_image_mass_edit'),
 
     url(r'^track/(?P<slug>[^/]*)/$', PublishTrackFeed()),
 )
index 01e4d1f..237e4a7 100644 (file)
@@ -426,62 +426,86 @@ def chunk_edit(request, slug, chunk):
 
 @transaction.commit_on_success
 @login_required
+@require_POST
 def chunk_mass_edit(request):
-    if request.method == 'POST':
-        ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
-        chunks = map(lambda i: Chunk.objects.get(id=i), ids)
-        
-        stage = request.POST.get('stage')
-        if stage:
-            try:
-                stage = Chunk.tag_model.objects.get(slug=stage)
-            except Chunk.DoesNotExist, e:
-                stage = None
-           
-            for c in chunks: c.stage = stage
-
-        username = request.POST.get('user')
-        logger.info("username: %s" % username)
-        logger.info(request.POST)
-        if username:
-            try:
-                user = User.objects.get(username=username)
-            except User.DoesNotExist, e:
-                user = None
-                
-            for c in chunks: c.user = user
-
-        status = request.POST.get('status')
-        if status:
-            books_affected = set()
-            for c in chunks:
-                if status == 'publish':
-                    c.head.publishable = True
-                    c.head.save()
-                elif status == 'unpublish':
-                    c.head.publishable = False
-                    c.head.save()
-                c.touch()  # cache
-                books_affected.add(c.book)
-            for b in books_affected:
-                b.touch()  # cache
-
-        project_id = request.POST.get('project')
-        if project_id:
-            try:
-                project = Project.objects.get(pk=int(project_id))
-            except (Project.DoesNotExist, ValueError), e:
-                project = None
-            for c in chunks:
-                book = c.book
-                book.project = project
-                book.save()
-
-        for c in chunks: c.save()
-
-        return HttpResponse("", content_type="text/plain")
-    else:
-        raise Http404
+    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
+    chunks = map(lambda i: Chunk.objects.get(id=i), ids)
+    
+    stage = request.POST.get('stage')
+    if stage:
+        try:
+            stage = Chunk.tag_model.objects.get(slug=stage)
+        except Chunk.DoesNotExist, e:
+            stage = None
+       
+        for c in chunks: c.stage = stage
+
+    username = request.POST.get('user')
+    logger.info("username: %s" % username)
+    logger.info(request.POST)
+    if username:
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist, e:
+            user = None
+            
+        for c in chunks: c.user = user
+
+    project_id = request.POST.get('project')
+    if project_id:
+        try:
+            project = Project.objects.get(pk=int(project_id))
+        except (Project.DoesNotExist, ValueError), e:
+            project = None
+        for c in chunks:
+            book = c.book
+            book.project = project
+            book.save()
+
+    for c in chunks: c.save()
+
+    return HttpResponse("", content_type="text/plain")
+
+
+@transaction.commit_on_success
+@login_required
+@require_POST
+def image_mass_edit(request):
+    ids = map(int, filter(lambda i: i.strip()!='', request.POST.get('ids').split(',')))
+    images = map(lambda i: Image.objects.get(id=i), ids)
+    
+    stage = request.POST.get('stage')
+    if stage:
+        try:
+            stage = Image.tag_model.objects.get(slug=stage)
+        except Image.DoesNotExist, e:
+            stage = None
+       
+        for c in images: c.stage = stage
+
+    username = request.POST.get('user')
+    logger.info("username: %s" % username)
+    logger.info(request.POST)
+    if username:
+        try:
+            user = User.objects.get(username=username)
+        except User.DoesNotExist, e:
+            user = None
+            
+        for c in images: c.user = user
+
+    project_id = request.POST.get('project')
+    if project_id:
+        try:
+            project = Project.objects.get(pk=int(project_id))
+        except (Project.DoesNotExist, ValueError), e:
+            project = None
+        for c in images:
+            c.project = project
+
+    for c in images: c.save()
+
+    return HttpResponse("", content_type="text/plain")
 
 
 @permission_required('catalogue.change_book')
index 9dd1e41..9d2511d 100644 (file)
 (function($) {
     $(function() {
-       // clicking on book checks chunks, too
-       $("input[name=select_book]").change(function(ev) {
-           $book = $(this);
-           $book.closest("table").find("input[name=select_chunk][data-book-id=" + $book.val() + "]").attr("checked", $book.is(':checked'));
-           });
 
-       // initialize context menu
-
-       var get_ids = function() {
-           return $.map($("input[name=select_chunk]:checked"), function(ele, idx) {
-               return ele.value;
-               }).concat(
-                   $.map($("input[name=select_book][data-chunk-id!=]:checked"), function(ele, idx) {
-                       return $(ele).attr("data-chunk-id");
-                       })).join();
-       };
-    
-
-       var set_field = function(key, ops) {
-            var fds = {}
-            fds.stage = "";
-            fds.user = "";
-            fds.status = "";
-
-           if (key == "publish" || key == "unpublish") {
-               fds["status"] = key;
-           } else {
-               var kp = key.split('_');
-               var field = kp[0];
-               var idx = parseInt(kp[1]);
+    // clicking on book checks chunks, too
+    $("input[name=select_book]").change(function(ev) {
+        $book = $(this);
+        $book.closest("table").find("input[name=select_chunk][data-book-id=" + $book.val() + "]").attr("checked", $book.is(':checked'));
+    });
 
-               var target_field = field;
-               if (field == 'other-user')
-                   target_field = 'user';
+    // initialize context menu
 
-               fds[target_field] = $("select[name="+field+"] option[value!=]").eq(idx).val();
-           }
-           /* fill in the form */
-            $("#chunk_mass_edit [name=ids]").val(get_ids());
-            for (var fn in fds) {
-                $("#chunk_mass_edit [name="+fn+"]").val(fds[fn]);
-            }
+   var get_ids = function() {
+       return $.map($("input[name=select_chunk]:checked"), function(ele, idx) {
+           return ele.value;
+           }).concat(
+               $.map($("input[name=select_book][data-chunk-id!=]:checked"), function(ele, idx) {
+                   return $(ele).attr("data-chunk-id");
+                   })).join();
+   };
 
-            $.post($("#chunk_mass_edit").attr("action"),
-                   $("#chunk_mass_edit").serialize(),
-                   function(data, status) {
-                       location.reload(true);
-                   }
-                  );
+    var get_callback = function(form_field_name) {
+        var $form = $("#chunk_mass_edit");
+        var $field = $("[name=" + form_field_name + "]", $form);
+        var $ids_field = $("[name=ids]").val(get_ids());
+        var usable_callback = function(value) {
+            $field.val(value);
+            $ids_field.val(get_ids());
+            $.post($form.attr("action"),
+               $form.serialize(),
+               function(data, status) {
+                   location.reload(true);
+               }
+            );
             return true;
-
-           
-       };
-
-        var get_items = function(field) {
-           var d = {};
-            $.each($("select[name="+field+"] option[value!=]"),
-                         function(idx, ele) {
-                            d[field+"_"+idx] = { name: $(ele).text() };
-                        });
-           return d;
         };
+        return usable_callback;
+    };
 
+    var get_items = function(field, callback) {
+        var d = {};
+        $.each($("select[name="+field+"] option[value!=]"),
+            function(idx, ele) {
+                d[field + "_" + idx] = {
+                    name: $(ele).text(), 
+                    callback: function() {callback($(ele).attr('value'));}
+                };
+            });
+        return d;
+    };
 
-       $.contextMenu({
-           selector: '#file-list',
-           items: {
-               "stage": { 
-                   name: $("label[for=mass_edit_stage]").text(),
-                   items: get_items("stage"),
-                   icon: "clock",
-               },
-               "user": { 
-                   name: $("label[for=mass_edit_user]").text(),
-                    items: (function() {
-                       var active_users = get_items("user");
-                       active_users['other'] = {
-                           name: $("label[for=mass_edit_other]").text(),
-                           items: get_items("other-user"),
-                       };
-                       return active_users;
-                       })(),
-                   icon: "user",
-                },
-                "publish": {
-                    name:  $("label[for=mass_edit_publish]").text(),
-                   icon: "ok",
-                },
-        "project" :{
-            name: $("label[for=mass_edit_project]").text(),
-            items: get_items("project"),
+    var user_callback = get_callback('user');
+    var users = [
+        get_items("user", user_callback),
+        {sep: '----'},
+        get_items("other-user", user_callback)
+    ];
+    var current_user_items = user_items = {};
+    var i = 0;
+    var more_label = $("label[for=mass_edit_more_users]").text();
+    for (user_table in users) {
+        for (user in users[user_table]) {
+            if (i && i % 20 == 0) {
+                var more_items = {};
+                current_user_items['more'] = {
+                    name: more_label,
+                    items: more_items
+                };
+                current_user_items = more_items;
+            }
+            current_user_items[user] = users[user_table][user];
+            i += 1;
+        }
+    }
+    $.contextMenu({
+        selector: '#file-list',
+        items: {
+            stage: { 
+                name: $("label[for=mass_edit_stage]").text(),
+                items: get_items("stage", get_callback('stage')),
+                icon: "clock",
+            },
+            user: { 
+                name: $("label[for=mass_edit_user]").text(),
+                items: user_items,
+                icon: "user",
             },
-        "publish": {
-            name:  $("label[for=mass_edit_publish]").text(),
-            icon: "ok",
+            project: {
+                name: $("label[for=mass_edit_project]").text(),
+                items: get_items("project", get_callback('project')),
             },
-               "unpublish": {
-                   name:  $("label[for=mass_edit_unpublish]").text(),
-                   icon: "stop",
-               },
-           },
-           callback: set_field,
-       });
-       
+        },
+    });
+
+
     });
 })(jQuery);
index 81cd316..cac7e48 100644 (file)
                this.revision = $("*[data-key='revision']", meta).text();
                this.readonly = !!$("*[data-key='readonly']", meta).text();
 
+        var diff = $("*[data-key='diff']", meta).text();
+        if (diff) {
+            diff = diff.split(',');
+            if (diff.length == 2 && diff[0] < diff[1])
+                this.diff = diff;
+            else if (diff.length == 1) {
+                diff = parseInt(diff);
+                if (diff != NaN)
+                    this.diff = [diff - 1, diff];
+            }
+        }
+
                this.text = null;
                this.has_local_changes = false;
                this._lock = -1;