text splitted into chunks,
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 20 May 2011 13:30:30 +0000 (15:30 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Fri, 20 May 2011 13:30:30 +0000 (15:30 +0200)
parts can be automagically joined (but no parts adding mechanism yet),
some URL changes

18 files changed:
apps/dvcs/migrations/0001_initial.py
apps/dvcs/models.py
apps/wiki/admin.py
apps/wiki/migrations/0003_auto__add_book.py [deleted file]
apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py [new file with mode: 0644]
apps/wiki/models.py
apps/wiki/templates/wiki/book_detail.html [new file with mode: 0755]
apps/wiki/templates/wiki/document_details_base.html
apps/wiki/templates/wiki/document_list.html
apps/wiki/templates/wiki/tabs/history_view.html
apps/wiki/templates/wiki/tabs/source_editor.html
apps/wiki/templates/wiki/tabs/summary_view.html
apps/wiki/urls.py
apps/wiki/views.py
redakcja/static/css/filelist.css
redakcja/static/js/wiki/wikiapi.js
redakcja/static/js/wiki/xslt.js
redakcja/static/xsl/wl2html_client.xsl

index c5e75c9..0e01d03 100644 (file)
@@ -14,13 +14,17 @@ class Migration(SchemaMigration):
             ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
             ('patch', self.gf('django.db.models.fields.TextField')(blank=True)),
             ('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'])),
             ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
             ('patch', self.gf('django.db.models.fields.TextField')(blank=True)),
             ('tree', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'])),
+            ('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['dvcs.Change'])),
             ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['dvcs.Change'])),
             ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
             ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['dvcs.Change'])),
             ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['dvcs.Change'])),
             ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
-            ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+            ('created_at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, db_index=True, blank=True)),
         ))
         db.send_create_signal('dvcs', ['Change'])
 
         ))
         db.send_create_signal('dvcs', ['Change'])
 
+        # Adding unique constraint on 'Change', fields ['tree', 'revision']
+        db.create_unique('dvcs_change', ['tree_id', 'revision'])
+
         # Adding model 'Document'
         db.create_table('dvcs_document', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
         # Adding model 'Document'
         db.create_table('dvcs_document', (
             ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
@@ -32,6 +36,9 @@ class Migration(SchemaMigration):
 
     def backwards(self, orm):
         
 
     def backwards(self, orm):
         
+        # Removing unique constraint on 'Change', fields ['tree', 'revision']
+        db.delete_unique('dvcs_change', ['tree_id', 'revision'])
+
         # Deleting model 'Change'
         db.delete_table('dvcs_change')
 
         # Deleting model 'Change'
         db.delete_table('dvcs_change')
 
@@ -77,14 +84,15 @@ class Migration(SchemaMigration):
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         },
         'dvcs.change': {
             'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
         },
         'dvcs.change': {
-            'Meta': {'ordering': "('created_at',)", 'object_name': 'Change'},
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
             'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
             'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
             'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
             '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['dvcs.Change']"}),
             'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
             'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
             'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
             '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['dvcs.Change']"}),
             'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
             'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
             'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
         },
         'dvcs.document': {
             'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
         },
         'dvcs.document': {
index 1c30387..9c9d350 100644 (file)
@@ -15,6 +15,7 @@ class Change(models.Model):
     author = models.ForeignKey(User, null=True, blank=True)
     patch = models.TextField(blank=True)
     tree = models.ForeignKey('Document')
     author = models.ForeignKey(User, null=True, blank=True)
     patch = models.TextField(blank=True)
     tree = models.ForeignKey('Document')
+    revision = models.IntegerField(db_index=True)
 
     parent = models.ForeignKey('self',
                         null=True, blank=True, default=None,
 
     parent = models.ForeignKey('self',
                         null=True, blank=True, default=None,
@@ -25,14 +26,23 @@ class Change(models.Model):
                         related_name="merge_children")
 
     description = models.TextField(blank=True, default='')
                         related_name="merge_children")
 
     description = models.TextField(blank=True, default='')
-    created_at = models.DateTimeField(auto_now_add=True)
+    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
 
     class Meta:
         ordering = ('created_at',)
 
     class Meta:
         ordering = ('created_at',)
+        unique_together = ['tree', 'revision']
 
     def __unicode__(self):
         return u"Id: %r, Tree %r, Parent %r, Patch '''\n%s'''" % (self.id, self.tree_id, self.parent_id, self.patch)
 
 
     def __unicode__(self):
         return u"Id: %r, Tree %r, Parent %r, Patch '''\n%s'''" % (self.id, self.tree_id, self.parent_id, self.patch)
 
+    def save(self, *args, **kwargs):
+        """
+            take the next available revision number if none yet
+        """
+        if self.revision is None:
+            self.revision = self.tree.revision() + 1
+        return super(Change, self).save(*args, **kwargs)
+
     @staticmethod
     def make_patch(src, dst):
         if isinstance(src, unicode):
     @staticmethod
     def make_patch(src, dst):
         if isinstance(src, unicode):
@@ -46,10 +56,9 @@ class Change(models.Model):
         if self.parent is None and self.merge_parent is not None:
             return self.apply_to(self.merge_parent.materialize())
 
         if self.parent is None and self.merge_parent is not None:
             return self.apply_to(self.merge_parent.materialize())
 
-        changes = Change.objects.filter(
-                        ~models.Q(parent=None) | models.Q(merge_parent=None),
+        changes = Change.objects.exclude(parent=None).filter(
                         tree=self.tree,
                         tree=self.tree,
-                        created_at__lte=self.created_at).order_by('created_at')
+                        revision__lte=self.revision).order_by('revision')
         text = u''
         for change in changes:
             text = change.apply_to(text)
         text = u''
         for change in changes:
             text = change.apply_to(text)
@@ -99,15 +108,6 @@ class Document(models.Model):
                     null=True, blank=True, default=None,
                     help_text=_("This document's current head."))
 
                     null=True, blank=True, default=None,
                     help_text=_("This document's current head."))
 
-    @classmethod
-    def create(cls, text='', *args, **kwargs):
-        instance = cls(*args, **kwargs)
-        instance.save()
-        head = instance.head
-        head.patch = Change.make_patch('', text)
-        head.save()
-        return instance
-
     def __unicode__(self):
         return u"{0}, HEAD: {1}".format(self.id, self.head_id)
 
     def __unicode__(self):
         return u"{0}, HEAD: {1}".format(self.id, self.head_id)
 
@@ -144,26 +144,30 @@ class Document(models.Model):
                 raise ValueError("You can provide only text or patch - not both")
             patch = kwargs['patch']
 
                 raise ValueError("You can provide only text or patch - not both")
             patch = kwargs['patch']
 
+        author = kwargs.get('author', None)
+
         old_head = self.head
         if parent != old_head:
         old_head = self.head
         if parent != old_head:
-            change = parent.make_merge_child(patch, kwargs['author'], kwargs.get('description', ''))
+            change = parent.make_merge_child(patch, author, kwargs.get('description', ''))
             # not Fast-Forward - perform a merge
             # not Fast-Forward - perform a merge
-            self.head = old_head.merge_with(change, author=kwargs['author'])
+            self.head = old_head.merge_with(change, author=author)
         else:
         else:
-            self.head = parent.make_child(patch, kwargs['author'], kwargs.get('description', ''))
+            self.head = parent.make_child(patch, author, kwargs.get('description', ''))
 
         self.save()
         return self.head
 
     def history(self):
 
         self.save()
         return self.head
 
     def history(self):
-        return self.change_set.all()
+        return self.change_set.filter(revision__gt=0)
 
     def revision(self):
 
     def revision(self):
-        return self.change_set.all().count()
+        rev = self.change_set.aggregate(
+                models.Max('revision'))['revision__max']
+        return rev if rev is not None else 0
 
     def at_revision(self, rev):
         if rev:
 
     def at_revision(self, rev):
         if rev:
-            return self.change_set.all()[rev-1]
+            return self.change_set.get(revision=rev)
         else:
             return self.head
 
         else:
             return self.head
 
@@ -171,6 +175,7 @@ class Document(models.Model):
     def listener_initial_commit(sender, instance, created, **kwargs):
         if created:
             instance.head = Change.objects.create(
     def listener_initial_commit(sender, instance, created, **kwargs):
         if created:
             instance.head = Change.objects.create(
+                    revision=0,
                     author=instance.creator,
                     patch=Change.make_patch('', ''),
                     tree=instance)
                     author=instance.creator,
                     patch=Change.make_patch('', ''),
                     tree=instance)
index 60a78f4..9725d0b 100644 (file)
@@ -2,5 +2,10 @@ from django.contrib import admin
 
 from wiki import models
 
 
 from wiki import models
 
-admin.site.register(models.Book)
+class BookAdmin(admin.ModelAdmin):
+    prepopulated_fields = {'slug': ['title']}
+
+
+admin.site.register(models.Book, BookAdmin)
+admin.site.register(models.Chunk)
 admin.site.register(models.Theme)
 admin.site.register(models.Theme)
diff --git a/apps/wiki/migrations/0003_auto__add_book.py b/apps/wiki/migrations/0003_auto__add_book.py
deleted file mode 100644 (file)
index 1c57004..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-# encoding: 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 'Book'
-        db.create_table('wiki_book', (
-            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
-            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=255, db_index=True)),
-            ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-            ('doc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'])),
-            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
-        ))
-        db.send_create_signal('wiki', ['Book'])
-
-
-    def backwards(self, orm):
-        
-        # Deleting model 'Book'
-        db.delete_table('wiki_book')
-
-
-    models = {
-        'auth.group': {
-            'Meta': {'object_name': 'Group'},
-            '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
-        },
-        'auth.permission': {
-            'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
-            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
-            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
-        },
-        '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': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
-            '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
-            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
-        },
-        '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'}),
-            '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'})
-        },
-        'dvcs.change': {
-            'Meta': {'ordering': "('created_at',)", 'object_name': 'Change'},
-            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
-            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
-            '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['dvcs.Change']"}),
-            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
-            'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
-            'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
-        },
-        'dvcs.document': {
-            'Meta': {'object_name': 'Document'},
-            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
-            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
-        },
-        'wiki.book': {
-            'Meta': {'ordering': "['title']", 'object_name': 'Book'},
-            'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"}),
-            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '255', 'db_index': 'True'}),
-            'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
-        },
-        'wiki.theme': {
-            'Meta': {'ordering': "('name',)", 'object_name': 'Theme'},
-            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
-            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
-        }
-    }
-
-    complete_apps = ['wiki']
diff --git a/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py b/apps/wiki/migrations/0003_auto__add_book__add_chunk__add_unique_chunk_book_number__add_unique_ch.py
new file mode 100644 (file)
index 0000000..39154ac
--- /dev/null
@@ -0,0 +1,135 @@
+# encoding: 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 'Book'
+        db.create_table('wiki_book', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('title', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=128, db_index=True)),
+            ('gallery', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+            ('parent', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='children', null=True, to=orm['wiki.Book'])),
+            ('parent_number', self.gf('django.db.models.fields.IntegerField')(db_index=True, null=True, blank=True)),
+        ))
+        db.send_create_signal('wiki', ['Book'])
+
+        # Adding model 'Chunk'
+        db.create_table('wiki_chunk', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('book', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['wiki.Book'])),
+            ('number', self.gf('django.db.models.fields.IntegerField')()),
+            ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, db_index=True)),
+            ('comment', self.gf('django.db.models.fields.CharField')(max_length=255)),
+            ('doc', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['dvcs.Document'], unique=True)),
+        ))
+        db.send_create_signal('wiki', ['Chunk'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'number']
+        db.create_unique('wiki_chunk', ['book_id', 'number'])
+
+        # Adding unique constraint on 'Chunk', fields ['book', 'slug']
+        db.create_unique('wiki_chunk', ['book_id', 'slug'])
+
+
+    def backwards(self, orm):
+        
+        # Removing unique constraint on 'Chunk', fields ['book', 'slug']
+        db.delete_unique('wiki_chunk', ['book_id', 'slug'])
+
+        # Removing unique constraint on 'Chunk', fields ['book', 'number']
+        db.delete_unique('wiki_chunk', ['book_id', 'number'])
+
+        # Deleting model 'Book'
+        db.delete_table('wiki_book')
+
+        # Deleting model 'Chunk'
+        db.delete_table('wiki_chunk')
+
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'ordering': "('content_type__app_label', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        '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': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        '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'}),
+            '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'})
+        },
+        'dvcs.change': {
+            'Meta': {'ordering': "('created_at',)", 'unique_together': "(['tree', 'revision'],)", 'object_name': 'Change'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
+            '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['dvcs.Change']"}),
+            'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'blank': 'True', 'to': "orm['dvcs.Change']"}),
+            'patch': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'revision': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
+            'tree': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']"})
+        },
+        'dvcs.document': {
+            'Meta': {'object_name': 'Document'},
+            'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
+            'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['dvcs.Change']", 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+        },
+        'wiki.book': {
+            'Meta': {'ordering': "['parent_number', 'title']", 'object_name': 'Book'},
+            'gallery': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            '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['wiki.Book']"}),
+            'parent_number': ('django.db.models.fields.IntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'wiki.chunk': {
+            'Meta': {'ordering': "['number']", 'unique_together': "[['book', 'number'], ['book', 'slug']]", 'object_name': 'Chunk'},
+            'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Book']"}),
+            'comment': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'doc': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['dvcs.Document']", 'unique': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'number': ('django.db.models.fields.IntegerField', [], {}),
+            'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'})
+        },
+        'wiki.theme': {
+            'Meta': {'ordering': "('name',)", 'object_name': 'Theme'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '50'})
+        }
+    }
+
+    complete_apps = ['wiki']
index a926493..66f8a28 100644 (file)
@@ -3,6 +3,9 @@
 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
+import itertools
+import re
+
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 
 from django.db import models
 from django.utils.translation import ugettext_lazy as _
 
@@ -13,16 +16,22 @@ import logging
 logger = logging.getLogger("fnp.wiki")
 
 
 logger = logging.getLogger("fnp.wiki")
 
 
+RE_TRIM_BEGIN = re.compile("^<!-- TRIM_BEGIN -->$", re.M)
+RE_TRIM_END = re.compile("^<!-- TRIM_END -->$", re.M)
+
+
 class Book(models.Model):
     """ A document edited on the wiki """
 
 class Book(models.Model):
     """ A document edited on the wiki """
 
-    slug = models.SlugField(_('slug'), max_length=255, unique=True)
-    title = models.CharField(_('displayed title'), max_length=255, blank=True)
-    doc = models.ForeignKey(dvcs_models.Document, editable=False)
+    title = models.CharField(_('title'), max_length=255)
+    slug = models.SlugField(_('slug'), max_length=128, unique=True)
     gallery = models.CharField(_('scan gallery name'), max_length=255, blank=True)
 
     gallery = models.CharField(_('scan gallery name'), max_length=255, blank=True)
 
+    parent = models.ForeignKey('self', null=True, blank=True, verbose_name=_('parent'), related_name="children")
+    parent_number = models.IntegerField(_('parent number'), null=True, blank=True, db_index=True)
+
     class Meta:
     class Meta:
-        ordering = ['title']
+        ordering = ['parent_number', 'title']
         verbose_name = _('book')
         verbose_name_plural = _('books')
 
         verbose_name = _('book')
         verbose_name_plural = _('books')
 
@@ -31,20 +40,96 @@ class Book(models.Model):
 
     @classmethod
     def create(cls, creator=None, text=u'', *args, **kwargs):
 
     @classmethod
     def create(cls, creator=None, text=u'', *args, **kwargs):
+        """
+            >>> Book.create(slug='x', text='abc').materialize()
+            'abc'
+        """
         instance = cls(*args, **kwargs)
         instance = cls(*args, **kwargs)
-        instance.doc = dvcs_models.Document.create(creator=creator, text=text)
         instance.save()
         instance.save()
+        instance.chunk_set.all()[0].doc.commit(author=creator, text=text)
         return instance
 
         return instance
 
+    @staticmethod
+    def trim(text, trim_begin=True, trim_end=True):
+        """ 
+            Cut off everything before RE_TRIM_BEGIN and after RE_TRIM_END, so
+            that eg. one big XML file can be compiled from many small XML files.
+        """
+        if trim_begin:
+            text = RE_TRIM_BEGIN.split(text, maxsplit=1)[-1]
+        if trim_end:
+            text = RE_TRIM_END.split(text, maxsplit=1)[0]
+        return text
+
+    def materialize(self):
+        """ 
+            Get full text of the document compiled from chunks.
+            Takes the current versions of all texts for now, but it should
+            be possible to specify a tag or a point in time for compiling.
+
+            First non-empty text's beginning isn't trimmed,
+            and last non-empty text's end isn't trimmed.
+        """
+        texts = []
+        trim_begin = False
+        text = ''
+        for chunk in self.chunk_set.all():
+            next_text = chunk.doc.materialize()
+            if not next_text:
+                continue
+            if text:
+                # trim the end, because there's more non-empty text
+                # don't trim beginning, if `text' is the first non-empty part
+                texts.append(self.trim(text, trim_begin=trim_begin))
+                trim_begin = True
+            text = next_text
+        # don't trim the end, because there's no more text coming after `text'
+        # only trim beginning if it's not still the first non-empty
+        texts.append(self.trim(text, trim_begin=trim_begin, trim_end=False))
+        return "".join(texts)
+
     @staticmethod
     def listener_create(sender, instance, created, **kwargs):
     @staticmethod
     def listener_create(sender, instance, created, **kwargs):
-        if created and instance.doc is None:
-            instance.doc = dvcs_models.Document.objects.create()
-            instance.save()
+        if created:
+            instance.chunk_set.create(number=1, slug='1')
 
 models.signals.post_save.connect(Book.listener_create, sender=Book)
 
 
 
 models.signals.post_save.connect(Book.listener_create, sender=Book)
 
 
+class Chunk(models.Model):
+    """ An editable chunk of text. Every Book text is divided into chunks. """
+
+    book = models.ForeignKey(Book)
+    number = models.IntegerField()
+    slug = models.SlugField()
+    comment = models.CharField(max_length=255)
+    doc = models.ForeignKey(dvcs_models.Document, editable=False, unique=True, null=True)
+
+    class Meta:
+        unique_together = [['book', 'number'], ['book', 'slug']]
+        ordering = ['number']
+
+    def __unicode__(self):
+        return "%d-%d: %s" % (self.book_id, self.number, self.comment)
+
+    def save(self, *args, **kwargs):
+        if self.doc is None:
+            self.doc = dvcs_models.Document.objects.create()
+        super(Chunk, self).save(*args, **kwargs)
+
+    @classmethod
+    def get(cls, slug, chunk=None):
+        if chunk is None:
+            return cls.objects.get(book__slug=slug, number=1)
+        else:
+            return cls.objects.get(book__slug=slug, slug=chunk)
+
+    def pretty_name(self):
+        return "%s, %s (%d/%d)" % (self.book.title, self.comment, 
+                self.number, self.book.chunk_set.count())
+
+
+
 
 '''
 from wiki import settings, constants
 
 '''
 from wiki import settings, constants
diff --git a/apps/wiki/templates/wiki/book_detail.html b/apps/wiki/templates/wiki/book_detail.html
new file mode 100755 (executable)
index 0000000..ee645f1
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "wiki/base.html" %}
+{% load i18n %}
+
+{% block leftcolumn %}
+
+    <h1>{{ object.title }}</h1>
+
+    <table>
+    {% for chunk in object.chunk_set.all %}
+        <tr><td>{{ chunk.number }}</td><td>{{ chunk.slug }}</td></tr>
+    {% endfor %}
+    </table>
+
+{% endblock leftcolumn %}
+
+{% block rightcolumn %}
+{% endblock rightcolumn %}
index 183da3d..3eda939 100644 (file)
@@ -16,9 +16,9 @@
 
 {% block maincontent %}
 <div id="document-meta"
 
 {% block maincontent %}
 <div id="document-meta"
-       data-document-name="{{ book.slug }}" style="display:none">
+       data-book="{{ chunk.book.slug }}" data-chunk="{{ chunk.slug }}" style="display:none">
 
 
-       <span data-key="gallery">{{ book.gallery }}</span>
+       <span data-key="gallery">{{ chunk.book.gallery }}</span>
        <span data-key="revision">{{ revision }}</span>
 
        {% block meta-extra %} {% endblock %}
        <span data-key="revision">{{ revision }}</span>
 
        {% block meta-extra %} {% endblock %}
index 726eaf2..bec1556 100644 (file)
@@ -32,8 +32,23 @@ $(function() {
                <tbody>
        {% for book in books %}
             <tr>
                <tbody>
        {% for book in books %}
             <tr>
-               <td colspan="3"><a target="_blank" data-id="{{ book.slug }}"
-                                       href="{% url wiki_editor book.slug %}">{{ book.title }}</a></td>
+                <td><a target="_blank" data-id="{{ book.slug }}"
+                    href="{% url wiki_book book.slug %}">{{ book.title }}</a>
+                </td>
+                <td class='listitem-tools'>
+                    {% ifequal book.chunk_set.count 1 %}
+                        <a target="_blank" data-id="{{ book.slug }}"
+                                href="{% url wiki_editor book.slug %}">
+                                [{% trans "edit" %}]</a>
+                    {% else %}
+                        {% for chunk in book.chunk_set.all %}
+                            <a target="_blank" data-id="{{ book.slug }}"
+                                href="{% url wiki_editor book.slug chunk.slug %}">
+                                [<span class='chunkno'>{{ forloop.counter }}.</span>
+                                {{ chunk.comment }}</a>]<br/>
+                        {% endfor %}
+                    {% endifequal %}
+                </td>
                                <!-- placeholder </td> -->
                        </tr>
        {% endfor %}
                                <!-- placeholder </td> -->
                        </tr>
        {% endfor %}
@@ -46,8 +61,8 @@ $(function() {
        <div id="last-edited-list">
                <h2>{% trans "Your last edited documents" %}</h2>
            <ol>
        <div id="last-edited-list">
                <h2>{% trans "Your last edited documents" %}</h2>
            <ol>
-                       {% for slug, item in last_books %}
-                       <li><a href="{% url wiki_editor slug %}"
+                       {% for slugs, item in last_books %}
+                       <li><a href="{% url wiki_editor slugs.0 slugs.1 %}"
                                target="_blank">{{ item.title }}</a><br/><span class="date">({{ item.time|date:"H:i:s, d/m/Y" }})</span></li>
                        {% endfor %}
                </ol>
                                target="_blank">{{ item.title }}</a><br/><span class="date">({{ item.time|date:"H:i:s, d/m/Y" }})</span></li>
                        {% endfor %}
                </ol>
index 7d61f99..60c59a9 100644 (file)
@@ -9,7 +9,7 @@
                        data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
                <button id="open-preview-button" disabled="disabled"
                        data-enabled-when="1"
                        data-enabled-when="1" disabled="disabled">{% trans "Revert document" %}</button>
                <button id="open-preview-button" disabled="disabled"
                        data-enabled-when="1"
-                       data-basehref="{% url wiki_editor_readonly book.slug %}">{% trans "View version" %}</button>
+                       data-basehref="{% url wiki_editor_readonly chunk.book.slug chunk.slug %}">{% trans "View version" %}</button>
 
        </div>
     <div id="history-view">
 
        </div>
     <div id="history-view">
index 9c3558f..9b22825 100644 (file)
@@ -2,9 +2,4 @@
 <div id="source-editor" class="editor">
     {% if not readonly %}{% toolbar %}{% endif %}
     <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
 <div id="source-editor" class="editor">
     {% if not readonly %}{% toolbar %}{% endif %}
     <textarea id="codemirror_placeholder">&lt;br/&gt;</textarea>
-    <!-- <input type="hidden" name="name" value="{{ book.slug }}" />
-       <input type="hidden" name="author" value="annonymous" />
-       <input type="hidden" name="comment" value="no comment" />
-       <input type="hidden" name="revision" value="{{ book.doc.revision }}" />
-       -->
 </div>
 </div>
index 90c2429..283698b 100644 (file)
@@ -8,23 +8,23 @@
                <h2>
                        <label for="title">{% trans "Title" %}:</label>
                        <span data-ui-editable="true" data-edit-target="meta.displayTitle"
                <h2>
                        <label for="title">{% trans "Title" %}:</label>
                        <span data-ui-editable="true" data-edit-target="meta.displayTitle"
-                       >{{ book.title }}</span>
+                       >{{ chunk.pretty_name }}</span>
                </h2>
                <p>
                        <label>{% trans "Document ID" %}:</label>
                </h2>
                <p>
                        <label>{% trans "Document ID" %}:</label>
-                       <span>{{ book.slug }}</span>
+                       <span>{{ chunk.book.slug }}/{{ chunk.slug }}</span>
                </p>
                <p>
                        <label>{% trans "Current version" %}:</label>
                </p>
                <p>
                        <label>{% trans "Current version" %}:</label>
-                       {{ book.doc.revision }} ({{ book.doc.head.created_at }})
+                       {{ chunk.doc.revision }} ({{ chunk.doc.head.created_at }})
                <p>
                        <label>{% trans "Last edited by" %}:</label>
                <p>
                        <label>{% trans "Last edited by" %}:</label>
-                       {{ book.doc.head.author }}
+                       {{ chunk.doc.head.author }}
                </p>
                <p>
                        <label for="gallery">{% trans "Link to gallery" %}:</label>
                        <span data-ui-editable="true" data-edit-target="meta.galleryLink"
                </p>
                <p>
                        <label for="gallery">{% trans "Link to gallery" %}:</label>
                        <span data-ui-editable="true" data-edit-target="meta.galleryLink"
-                       >{{ book.gallery }}</span>
+                       >{{ chunk.book.gallery }}</span>
                </p>
 
                <p><button type="button" id="publish_button">{% trans "Publish" %}</button></p>
                </p>
 
                <p><button type="button" id="publish_button">{% trans "Publish" %}</button></p>
index ceb7416..7ed47a0 100644 (file)
@@ -1,7 +1,9 @@
 # -*- coding: utf-8
 from django.conf.urls.defaults import *
 from django.views.generic.simple import redirect_to
 # -*- coding: utf-8
 from django.conf.urls.defaults import *
 from django.views.generic.simple import redirect_to
+from django.views.generic.list_detail import object_detail
 from django.conf import settings
 from django.conf import settings
+from wiki.models import Book
 
 
 #PART = ur"""[ ĄĆĘŁŃÓŚŻŹąćęłńóśżź0-9\w_.-]+"""
 
 
 #PART = ur"""[ ĄĆĘŁŃÓŚŻŹąćęłńóśżź0-9\w_.-]+"""
@@ -14,39 +16,41 @@ urlpatterns = patterns('wiki.views',
     #url(r'^catalogue/([^/]+)/([^/]+)/$', 'document_list'),
     #url(r'^catalogue/([^/]+)/([^/]+)/([^/]+)$', 'document_list'),
 
     #url(r'^catalogue/([^/]+)/([^/]+)/$', 'document_list'),
     #url(r'^catalogue/([^/]+)/([^/]+)/([^/]+)$', 'document_list'),
 
-    url(r'^(?P<slug>[^/]+)/$',
+    url(r'^edit/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'editor', name="wiki_editor"),
 
         'editor', name="wiki_editor"),
 
-    url(r'^(?P<slug>[^/]+)/readonly$',
+    url(r'^readonly/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'editor_readonly', name="wiki_editor_readonly"),
 
     url(r'^upload/$',
         'upload', name='wiki_upload'),
 
         'editor_readonly', name="wiki_editor_readonly"),
 
     url(r'^upload/$',
         'upload', name='wiki_upload'),
 
-    url(r'^create/(?P<slug>[^/]+)',
+    url(r'^create/(?P<slug>[^/]*)/',
         'create_missing', name='wiki_create_missing'),
 
         'create_missing', name='wiki_create_missing'),
 
-    url(r'^(?P<directory>[^/]+)/gallery$',
+    url(r'^gallery/(?P<directory>[^/]+)/$',
         'gallery', name="wiki_gallery"),
 
         'gallery', name="wiki_gallery"),
 
-    url(r'^(?P<slug>[^/]+)/history$',
+    url(r'^history/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'history', name="wiki_history"),
 
         'history', name="wiki_history"),
 
-    url(r'^(?P<slug>[^/]+)/rev$',
+    url(r'^rev/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'revision', name="wiki_revision"),
 
         'revision', name="wiki_revision"),
 
-    url(r'^(?P<slug>[^/]+)/text$',
+    url(r'^text/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'text', name="wiki_text"),
 
         'text', name="wiki_text"),
 
-    url(r'^(?P<slug>[^/]+)/revert$',
+    url(r'^revert/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$',
         'revert', name='wiki_revert'),
 
     #url(r'^(?P<name>[^/]+)/publish$', 'publish', name="wiki_publish"),
     #url(r'^(?P<name>[^/]+)/publish/(?P<version>\d+)$', 'publish', name="wiki_publish"),
 
         'revert', name='wiki_revert'),
 
     #url(r'^(?P<name>[^/]+)/publish$', 'publish', name="wiki_publish"),
     #url(r'^(?P<name>[^/]+)/publish/(?P<version>\d+)$', 'publish', name="wiki_publish"),
 
-    url(r'^(?P<slug>[^/]+)/diff$', 'diff', name="wiki_diff"),
+    url(r'^diff/(?P<slug>[^/]+)/(?:(?P<chunk>[^/]+)/)?$', 'diff', name="wiki_diff"),
     #url(r'^(?P<name>[^/]+)/tags$', 'add_tag', name="wiki_add_tag"),
 
     #url(r'^(?P<name>[^/]+)/tags$', 'add_tag', name="wiki_add_tag"),
 
+    url(r'^full/(?P<slug>[^/]+)/$', 'compiled', name="wiki_compiled"),
 
 
-
+    url(r'^book/(?P<slug>[^/]+)/$', object_detail, 
+        {"queryset": Book.objects.all()}, name="wiki_book"),
 )
 )
index 6f09ddd..45ded5b 100644 (file)
@@ -11,8 +11,9 @@ from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
                 ajax_require_permission, recursive_groupby)
 from django import http
 from django.shortcuts import get_object_or_404, redirect
                 ajax_require_permission, recursive_groupby)
 from django import http
 from django.shortcuts import get_object_or_404, redirect
+from django.http import Http404
 
 
-from wiki.models import Book, Theme
+from wiki.models import Book, Chunk, Theme
 from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm
 from datetime import datetime
 from django.utils.encoding import smart_unicode
 from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm
 from datetime import datetime
 from django.utils.encoding import smart_unicode
@@ -42,26 +43,35 @@ def document_list(request):
 
 
 @never_cache
 
 
 @never_cache
-def editor(request, slug, template_name='wiki/document_details.html'):
+def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
     try:
     try:
-        book = Book.objects.get(slug=slug)
-    except Book.DoesNotExist:
-        return http.HttpResponseRedirect(reverse("wiki_create_missing", args=[slug]))
+        chunk = Chunk.get(slug, chunk)
+    except Chunk.MultipleObjectsReturned:
+        # TODO: choice page
+        raise Http404
+    except Chunk.DoesNotExist:
+        if chunk is None:
+            try:
+                book = Book.objects.get(slug=slug)
+            except Book.DoesNotExist:
+                return http.HttpResponseRedirect(reverse("wiki_create_missing", args=[slug]))
+        else:
+            raise Http404
 
     access_time = datetime.now()
     last_books = request.session.get("wiki_last_books", {})
 
     access_time = datetime.now()
     last_books = request.session.get("wiki_last_books", {})
-    last_books[slug] = {
+    last_books[slug, chunk.slug] = {
         'time': access_time,
         'time': access_time,
-        'title': book.title,
+        'title': chunk.pretty_name(),
         }
 
     if len(last_books) > MAX_LAST_DOCS:
         }
 
     if len(last_books) > MAX_LAST_DOCS:
-        oldest_key = min(last_books, key=operator.itemgetter('time'))
+        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
         del last_books[oldest_key]
     request.session['wiki_last_books'] = last_books
 
     return direct_to_template(request, template_name, extra_context={
         del last_books[oldest_key]
     request.session['wiki_last_books'] = last_books
 
     return direct_to_template(request, template_name, extra_context={
-        'book': book,
+        'chunk': chunk,
         'forms': {
             "text_save": DocumentTextSaveForm(prefix="textsave"),
             "text_revert": DocumentTextRevertForm(prefix="textrevert"),
         'forms': {
             "text_save": DocumentTextSaveForm(prefix="textsave"),
             "text_revert": DocumentTextRevertForm(prefix="textrevert"),
@@ -72,27 +82,27 @@ def editor(request, slug, template_name='wiki/document_details.html'):
 
 
 @require_GET
 
 
 @require_GET
-def editor_readonly(request, slug, template_name='wiki/document_details_readonly.html'):
+def editor_readonly(request, slug, chunk=None, template_name='wiki/document_details_readonly.html'):
     try:
     try:
-        book = Book.objects.get(slug=slug)
+        chunk = Chunk.get(slug, chunk)
         revision = request.GET['revision']
         revision = request.GET['revision']
-    except KeyError:
-        raise http.Http404
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist, KeyError):
+        raise Http404
 
     access_time = datetime.now()
     last_books = request.session.get("wiki_last_books", {})
 
     access_time = datetime.now()
     last_books = request.session.get("wiki_last_books", {})
-    last_books[slug] = {
+    last_books[slug, chunk.slug] = {
         'time': access_time,
         'time': access_time,
-        'title': book.title,
+        'title': chunk.book.title,
         }
 
     if len(last_books) > MAX_LAST_DOCS:
         }
 
     if len(last_books) > MAX_LAST_DOCS:
-        oldest_key = min(last_books, key=operator.itemgetter('time'))
+        oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
         del last_books[oldest_key]
     request.session['wiki_last_books'] = last_books
 
     return direct_to_template(request, template_name, extra_context={
         del last_books[oldest_key]
     request.session['wiki_last_books'] = last_books
 
     return direct_to_template(request, template_name, extra_context={
-        'book': book,
+        'chunk': chunk,
         'revision': revision,
         'readonly': True,
         'REDMINE_URL': settings.REDMINE_URL,
         'revision': revision,
         'readonly': True,
         'REDMINE_URL': settings.REDMINE_URL,
@@ -189,8 +199,11 @@ def upload(request):
 
 @never_cache
 @decorator_from_middleware(GZipMiddleware)
 
 @never_cache
 @decorator_from_middleware(GZipMiddleware)
-def text(request, slug):
-    doc = get_object_or_404(Book, slug=slug).doc
+def text(request, slug, chunk=None):
+    try:
+        doc = Chunk.get(slug, chunk).doc
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
 
     if request.method == 'POST':
         form = DocumentTextSaveForm(request.POST, prefix="textsave")
 
     if request.method == 'POST':
         form = DocumentTextSaveForm(request.POST, prefix="textsave")
@@ -223,7 +236,7 @@ def text(request, slug):
         
         try:
             revision = int(revision)
         
         try:
             revision = int(revision)
-        except ValueError:
+        except (ValueError, TypeError):
             revision = None
 
         return JSONResponse({
             revision = None
 
         return JSONResponse({
@@ -233,12 +246,25 @@ def text(request, slug):
         })
 
 
         })
 
 
+@never_cache
+def compiled(request, slug):
+    text = get_object_or_404(Book, slug=slug).materialize()
+    
+    response = http.HttpResponse(text, content_type='application/xml', mimetype='application/wl+xml')
+    response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
+    return response
+
+
 @never_cache
 @require_POST
 @never_cache
 @require_POST
-def revert(request, slug):
+def revert(request, slug, chunk=None):
     form = DocumentTextRevertForm(request.POST, prefix="textrevert")
     if form.is_valid():
     form = DocumentTextRevertForm(request.POST, prefix="textrevert")
     if form.is_valid():
-        doc = get_object_or_404(Book, slug=slug).doc
+        try:
+            doc = Chunk.get(slug, chunk).doc
+        except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+            raise Http404
+
         revision = form.cleaned_data['revision']
 
         comment = form.cleaned_data['comment']
         revision = form.cleaned_data['revision']
 
         comment = form.cleaned_data['comment']
@@ -290,7 +316,7 @@ def gallery(request, directory):
 
 
 @never_cache
 
 
 @never_cache
-def diff(request, slug):
+def diff(request, slug, chunk=None):
     revA = int(request.GET.get('from', 0))
     revB = int(request.GET.get('to', 0))
 
     revA = int(request.GET.get('from', 0))
     revB = int(request.GET.get('to', 0))
 
@@ -300,7 +326,10 @@ def diff(request, slug):
     if revB == 0:
         revB = None
 
     if revB == 0:
         revB = None
 
-    doc = get_object_or_404(Book, slug=slug).doc
+    try:
+        doc = Chunk.get(slug, chunk).doc
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
     docA = doc.at_revision(revA).materialize()
     docB = doc.at_revision(revB).materialize()
 
     docA = doc.at_revision(revA).materialize()
     docB = doc.at_revision(revB).materialize()
 
@@ -309,18 +338,24 @@ def diff(request, slug):
 
 
 @never_cache
 
 
 @never_cache
-def revision(request, slug):
-    book = get_object_or_404(Book, slug=slug)
-    return http.HttpResponse(str(book.doc.revision()))
+def revision(request, slug, chunk=None):
+    try:
+        doc = Chunk.get(slug, chunk).doc
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
+    return http.HttpResponse(str(doc.revision()))
 
 
 @never_cache
 
 
 @never_cache
-def history(request, slug):
+def history(request, slug, chunk=None):
     # TODO: pagination
     # TODO: pagination
-    book = get_object_or_404(Book, slug=slug)
-    rev = book.doc.revision()
+    try:
+        doc = Chunk.get(slug, chunk).doc
+    except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
+        raise Http404
+
     changes = []
     changes = []
-    for change in book.doc.history().order_by('-created_at'):
+    for change in doc.history().order_by('-created_at'):
         if change.author:
             author = "%s %s <%s>" % (
                 change.author.first_name,
         if change.author:
             author = "%s %s <%s>" % (
                 change.author.first_name,
@@ -329,13 +364,12 @@ def history(request, slug):
         else:
             author = None
         changes.append({
         else:
             author = None
         changes.append({
-                "version": rev,
+                "version": change.revision,
                 "description": change.description,
                 "author": author,
                 "date": change.created_at,
                 "tag": [],
             })
                 "description": change.description,
                 "author": author,
                 "date": change.created_at,
                 "tag": [],
             })
-        rev -= 1
     return JSONResponse(changes)
 
 
     return JSONResponse(changes)
 
 
index 2ccf33e..f3de556 100644 (file)
@@ -86,4 +86,15 @@ a:hover {
 
 #skipped-list {
     color: #666;
 
 #skipped-list {
     color: #666;
-}
\ No newline at end of file
+}
+
+.chunkno {
+    font-size: .7em;
+}
+
+td {
+    vertical-align: top;
+}
+.listitem-tools {
+    padding-left: 2em;
+}
index 1fe49d4..d8ade93 100644 (file)
@@ -18,7 +18,7 @@
                var base_path = "/documents";
 
                if (vname == "ajax_document_text") {
                var base_path = "/documents";
 
                if (vname == "ajax_document_text") {
-                       var path = "/" + arguments[1] + "/text";
+                       var path = "/text/" + arguments[1];
 
                if (arguments[2] !== undefined)
                                path += "/" + arguments[2];
 
                if (arguments[2] !== undefined)
                                path += "/" + arguments[2];
                }
 
         if (vname == "ajax_document_revert") {
                }
 
         if (vname == "ajax_document_revert") {
-            return base_path + "/" + arguments[1] + "/revert";
+            return base_path + "/revert/" + arguments[1];
         }
 
 
                if (vname == "ajax_document_history") {
 
         }
 
 
                if (vname == "ajax_document_history") {
 
-                       return base_path + "/" + arguments[1] + "/history";
+                       return base_path + "/history/" + arguments[1];
                }
 
                if (vname == "ajax_document_gallery") {
 
                }
 
                if (vname == "ajax_document_gallery") {
 
-                       return base_path + "/" + arguments[1] + "/gallery";
+                       return base_path + "/gallery/" + arguments[1];
                }
 
                if (vname == "ajax_document_diff")
                }
 
                if (vname == "ajax_document_diff")
-                       return base_path + "/" + arguments[1] + "/diff";
+                       return base_path + "/diff/" + arguments[1];
 
         if (vname == "ajax_document_rev")
 
         if (vname == "ajax_document_rev")
-            return base_path + "/" + arguments[1] + "/rev";
+            return base_path + "/rev/" + arguments[1];
 
                if (vname == "ajax_document_addtag")
 
                if (vname == "ajax_document_addtag")
-                       return base_path + "/" + arguments[1] + "/tags";
+                       return base_path + "/tags/" + arguments[1];
 
                if (vname == "ajax_publish")
 
                if (vname == "ajax_publish")
-                       return base_path + "/" + arguments[1] + "/publish";
+                       return base_path + "/publish/" + arguments[1];
 
                console.log("Couldn't reverse match:", vname);
                return "/404.html";
 
                console.log("Couldn't reverse match:", vname);
                return "/404.html";
@@ -62,7 +62,7 @@
         */
        function WikiDocument(element_id) {
                var meta = $('#' + element_id);
         */
        function WikiDocument(element_id) {
                var meta = $('#' + element_id);
-               this.id = meta.attr('data-document-name');
+               this.id = meta.attr('data-book') + '/' + meta.attr('data-chunk');
 
                this.revision = $("*[data-key='revision']", meta).text();
                this.readonly = !!$("*[data-key='readonly']", meta).text();
 
                this.revision = $("*[data-key='revision']", meta).text();
                this.readonly = !!$("*[data-key='readonly']", meta).text();
index c7b846b..ce7614d 100644 (file)
@@ -17,7 +17,7 @@ function withStylesheets(code_block, onError)
     if (!xml2htmlStylesheet) {
        $.blockUI({message: 'Ładowanie arkuszy stylów...'});
        $.ajax({
     if (!xml2htmlStylesheet) {
        $.blockUI({message: 'Ładowanie arkuszy stylów...'});
        $.ajax({
-               url: STATIC_URL + 'xsl/wl2html_client.xsl?20110112',
+               url: STATIC_URL + 'xsl/wl2html_client.xsl?20110520',
                dataType: 'xml',
                timeout: 10000,
                success: function(data) {
                dataType: 'xml',
                timeout: 10000,
                success: function(data) {
@@ -255,6 +255,11 @@ HTMLSerializer.prototype.serialize = function(rootElement, stripOuter)
                                self.result += text_buffer;
                                text_buffer = token.node.nodeValue;
                                break;
                                self.result += text_buffer;
                                text_buffer = token.node.nodeValue;
                                break;
+            case COMMENT_NODE:
+                self.result += text_buffer;
+                text_buffer = '';
+                self.result += '<!--' + token.node.nodeValue + '-->';
+                break;
                };
        };
     self.result += text_buffer;
                };
        };
     self.result += text_buffer;
index c2aea5f..4f64291 100644 (file)
                 </xsl:apply-templates>
             </span>
     </xsl:template>
                 </xsl:apply-templates>
             </span>
     </xsl:template>
-</xsl:stylesheet>
\ No newline at end of file
+
+    <xsl:template match="comment()">
+        <xsl:comment><xsl:value-of select="."/></xsl:comment>
+    </xsl:template>
+</xsl:stylesheet>