From 7671cda4767353b2a93b05f2332ba6e2f236c468 Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Wed, 1 Jun 2011 12:03:43 +0200 Subject: [PATCH 1/1] changeset tagging in dvcs, document list items caching --- apps/dvcs/admin.py | 3 +- apps/dvcs/migrations/0002_auto__add_tag.py | 104 ++++++++++++++++++ apps/dvcs/models.py | 69 ++++++++++-- apps/wiki/forms.py | 8 +- .../0004_auto__add_field_book__list_html.py | 102 +++++++++++++++++ apps/wiki/models.py | 95 ++++++++-------- apps/wiki/templates/wiki/document_list.html | 23 +--- .../templates/wiki/document_list_item.html | 21 ++++ apps/wiki/urls.py | 2 +- apps/wiki/views.py | 45 ++++---- redakcja/static/js/wiki/view_history.js | 6 +- redakcja/static/js/wiki/wikiapi.js | 2 +- 12 files changed, 372 insertions(+), 108 deletions(-) create mode 100644 apps/dvcs/migrations/0002_auto__add_tag.py create mode 100644 apps/wiki/migrations/0004_auto__add_field_book__list_html.py create mode 100755 apps/wiki/templates/wiki/document_list_item.html diff --git a/apps/dvcs/admin.py b/apps/dvcs/admin.py index c81d3b7b..984798df 100644 --- a/apps/dvcs/admin.py +++ b/apps/dvcs/admin.py @@ -1,5 +1,6 @@ from django.contrib.admin import site -from dvcs.models import Document, Change +from dvcs.models import Document, Change, Tag +site.register(Tag) site.register(Document) site.register(Change) diff --git a/apps/dvcs/migrations/0002_auto__add_tag.py b/apps/dvcs/migrations/0002_auto__add_tag.py new file mode 100644 index 00000000..7f5b9a3e --- /dev/null +++ b/apps/dvcs/migrations/0002_auto__add_tag.py @@ -0,0 +1,104 @@ +# 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 'Tag' + db.create_table('dvcs_tag', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=64)), + ('slug', self.gf('django.db.models.fields.SlugField')(db_index=True, max_length=64, unique=True, null=True, blank=True)), + ('ordering', self.gf('django.db.models.fields.IntegerField')()), + )) + db.send_create_signal('dvcs', ['Tag']) + + # Adding M2M table for field tags on 'Change' + db.create_table('dvcs_change_tags', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('change', models.ForeignKey(orm['dvcs.change'], null=False)), + ('tag', models.ForeignKey(orm['dvcs.tag'], null=False)) + )) + db.create_unique('dvcs_change_tags', ['change_id', 'tag_id']) + + + def backwards(self, orm): + + # Deleting model 'Tag' + db.delete_table('dvcs_tag') + + # Removing M2M table for field tags on 'Change' + db.delete_table('dvcs_change_tags') + + + 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'}), + 'author_desc': ('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'}), + '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'}), + 'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['dvcs.Tag']", 'symmetrical': 'False'}), + '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'}) + }, + 'dvcs.tag': { + 'Meta': {'ordering': "['ordering']", 'object_name': 'Tag'}, + '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', [], {'db_index': 'True', 'max_length': '64', 'unique': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['dvcs'] diff --git a/apps/dvcs/models.py b/apps/dvcs/models.py index 7ac7cbef..6c5796af 100644 --- a/apps/dvcs/models.py +++ b/apps/dvcs/models.py @@ -6,6 +6,41 @@ from django.utils.translation import ugettext_lazy as _ from mercurial import mdiff, simplemerge import pickle + +class Tag(models.Model): + """ + a tag (e.g. document stage) which can be applied to a change + """ + + name = models.CharField(_('name'), max_length=64) + slug = models.SlugField(_('slug'), unique=True, max_length=64, + null=True, blank=True) + ordering = models.IntegerField(_('ordering')) + + _object_cache = {} + + class Meta: + ordering = ['ordering'] + + def __unicode__(self): + return self.name + + @classmethod + def get(cls, slug): + if slug in cls._object_cache: + return cls._object_cache[slug] + else: + obj = cls.objects.get(slug=slug) + cls._object_cache[slug] = obj + return obj + + @staticmethod + def listener_changed(sender, instance, **kwargs): + sender._object_cache = {} + +models.signals.pre_save.connect(Tag.listener_changed, sender=Tag) + + class Change(models.Model): """ Single document change related to previous change. The "parent" @@ -32,6 +67,8 @@ class Change(models.Model): created_at = models.DateTimeField(editable=False, db_index=True, default=datetime.now) + tags = models.ManyToManyField(Tag) + class Meta: ordering = ('created_at',) unique_together = ['tree', 'revision'] @@ -78,18 +115,26 @@ class Change(models.Model): text = change.apply_to(text) return text - def make_child(self, patch, description, author=None, author_desc=None): - return self.children.create(patch=patch, + def make_child(self, patch, description, author=None, + author_desc=None, tags=None): + ch = self.children.create(patch=patch, tree=self.tree, author=author, author_desc=author_desc, description=description) + if tags is not None: + ch.tags = tags + return ch def make_merge_child(self, patch, description, author=None, - author_desc=None): - return self.merge_children.create(patch=patch, + author_desc=None, tags=None): + ch = self.merge_children.create(patch=patch, tree=self.tree, author=author, author_desc=author_desc, - description=description) + description=description, + tags=tags) + if tags is not None: + ch.tags = tags + return ch def apply_to(self, text): return mdiff.patch(text, pickle.loads(self.patch.encode('ascii'))) @@ -165,19 +210,22 @@ class Document(models.Model): author = kwargs.get('author', None) author_desc = kwargs.get('author_desc', None) + tags = kwargs.get('tags', []) old_head = self.head if parent != old_head: change = parent.make_merge_child(patch, author=author, author_desc=author_desc, - description=kwargs.get('description', '')) + description=kwargs.get('description', ''), + tags=tags) # not Fast-Forward - perform a merge self.head = old_head.merge_with(change, author=author, author_desc=author_desc) else: self.head = parent.make_child(patch, author=author, author_desc=author_desc, - description=kwargs.get('description', '')) + description=kwargs.get('description', ''), + tags=tags) self.save() return self.head @@ -196,6 +244,13 @@ class Document(models.Model): else: return self.head + def last_tagged(self, tag): + changes = tag.change_set.filter(tree=self).order_by('-created_at')[:1] + if changes.count(): + return changes[0] + else: + return None + @staticmethod def listener_initial_commit(sender, instance, created, **kwargs): # run for Document and its subclasses diff --git a/apps/wiki/forms.py b/apps/wiki/forms.py index b057be05..f3362e81 100644 --- a/apps/wiki/forms.py +++ b/apps/wiki/forms.py @@ -4,10 +4,10 @@ # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information. # from django import forms -from wiki.constants import DOCUMENT_TAGS, DOCUMENT_STAGES from wiki.models import Book from django.utils.translation import ugettext_lazy as _ +from dvcs.models import Tag class DocumentTagForm(forms.Form): """ @@ -15,7 +15,7 @@ class DocumentTagForm(forms.Form): """ id = forms.CharField(widget=forms.HiddenInput) - tag = forms.ChoiceField(choices=DOCUMENT_TAGS) + tag = forms.ModelChoiceField(queryset=Tag.objects.all()) revision = forms.IntegerField(widget=forms.HiddenInput) @@ -98,8 +98,8 @@ class DocumentTextSaveForm(forms.Form): help_text=_(u"Describe changes you made."), ) - stage_completed = forms.ChoiceField( - choices=DOCUMENT_STAGES, + stage_completed = forms.ModelChoiceField( + queryset=Tag.objects.all(), required=False, label=_(u"Completed"), help_text=_(u"If you completed a life cycle stage, select it."), diff --git a/apps/wiki/migrations/0004_auto__add_field_book__list_html.py b/apps/wiki/migrations/0004_auto__add_field_book__list_html.py new file mode 100644 index 00000000..a09dc90e --- /dev/null +++ b/apps/wiki/migrations/0004_auto__add_field_book__list_html.py @@ -0,0 +1,102 @@ +# 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 field 'Book._list_html' + db.add_column('wiki_book', '_list_html', self.gf('django.db.models.fields.TextField')(null=True), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'Book._list_html' + db.delete_column('wiki_book', '_list_html') + + + 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'}), + 'author_desc': ('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'}), + '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'}, + '_list_html': ('django.db.models.fields.TextField', [], {'null': 'True'}), + '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', '_ormbases': ['dvcs.Document']}, + 'book': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Book']"}), + 'comment': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'document_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['dvcs.Document']", 'unique': 'True', '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'] diff --git a/apps/wiki/models.py b/apps/wiki/models.py index f8c9d258..60702065 100644 --- a/apps/wiki/models.py +++ b/apps/wiki/models.py @@ -8,7 +8,9 @@ import re from django.core.urlresolvers import reverse from django.db import models +from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ +from django.template.loader import render_to_string from dvcs import models as dvcs_models @@ -31,6 +33,11 @@ class Book(models.Model): 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) + _list_html = models.TextField(editable=False, null=True) + + class NoTextError(BaseException): + pass + class Meta: ordering = ['parent_number', 'title'] verbose_name = _('book') @@ -39,6 +46,11 @@ class Book(models.Model): def __unicode__(self): return self.title + def save(self, reset_list_html=True, *args, **kwargs): + if reset_list_html: + self._list_html = None + return super(Book, self).save(*args, **kwargs) + @classmethod def create(cls, creator=None, text=u'', *args, **kwargs): """ @@ -59,6 +71,14 @@ class Book(models.Model): def __len__(self): return self.chunk_set.count() + def list_html(self): + if self._list_html is None: + print 'rendering', self.title + self._list_html = render_to_string('wiki/document_list_item.html', + {'book': self}) + self.save(reset_list_html=False) + return mark_safe(self._list_html) + @staticmethod def trim(text, trim_begin=True, trim_end=True): """ @@ -71,7 +91,11 @@ class Book(models.Model): text = RE_TRIM_END.split(text, maxsplit=1)[0] return text - def materialize(self): + @staticmethod + def publish_tag(): + return dvcs_models.Tag.get('publish') + + def materialize(self, tag=None): """ Get full text of the document compiled from chunks. Takes the current versions of all texts for now, but it should @@ -80,10 +104,16 @@ class Book(models.Model): First non-empty text's beginning isn't trimmed, and last non-empty text's end isn't trimmed. """ + if tag: + changes = [chunk.last_tagged(tag) for chunk in self] + else: + changes = [chunk.head for chunk in self] + if None in changes: + raise self.NoTextError('Some chunks have no available text.') texts = [] trim_begin = False text = '' - for chunk in self: + for chunk in changes: next_text = chunk.materialize() if not next_text: continue @@ -98,6 +128,14 @@ class Book(models.Model): texts.append(self.trim(text, trim_begin=trim_begin, trim_end=False)) return "".join(texts) + def publishable(self): + if not len(self): + return False + for chunk in self: + if not chunk.publishable(): + return False + return True + @staticmethod def listener_create(sender, instance, created, **kwargs): if created: @@ -135,55 +173,18 @@ class Chunk(dvcs_models.Document): return "%s, %s (%d/%d)" % (self.book.title, self.comment, self.number, len(self.book)) + def publishable(self): + return self.last_tagged(Book.publish_tag()) + @staticmethod + def listener_saved(sender, instance, created, **kwargs): + if instance.book: + # save book so that its _list_html is reset + instance.book.save() - -''' -from wiki import settings, constants -from slughifi import slughifi - -from django.http import Http404 - - - - -class Document(object): - - def add_tag(self, tag, revision, author): - """ Add document specific tag """ - logger.debug("Adding tag %s to doc %s version %d", tag, self.name, revision) - self.storage.vstorage.add_page_tag(self.name, revision, tag, user=author) - - @property - def plain_text(self): - return re.sub(self.META_REGEX, '', self.text, 1) - - def meta(self): - result = {} - - m = re.match(self.META_REGEX, self.text) - if m: - for line in m.group(1).split('\n'): - try: - k, v = line.split(':', 1) - result[k.strip()] = v.strip() - except ValueError: - continue - - gallery = result.get('gallery', slughifi(self.name.replace(' ', '_'))) - - if gallery.startswith('/'): - gallery = os.path.basename(gallery) - - result['gallery'] = gallery - return result - - def info(self): - return self.storage.vstorage.page_meta(self.name, self.revision) - +models.signals.post_save.connect(Chunk.listener_saved, sender=Chunk) -''' class Theme(models.Model): name = models.CharField(_('name'), max_length=50, unique=True) diff --git a/apps/wiki/templates/wiki/document_list.html b/apps/wiki/templates/wiki/document_list.html index f808a1d3..25b4cf27 100644 --- a/apps/wiki/templates/wiki/document_list.html +++ b/apps/wiki/templates/wiki/document_list.html @@ -31,28 +31,7 @@ $(function() { {% for book in books %} - - - [?] - {% ifequal book.chunk_set.count 1 %} - - {{ book.title }} - {% else %} - {{ book.title }} -
- {% for chunk in book %} - - {{ forloop.counter }}. - {{ chunk.comment }}
- {% endfor %} -
- {% endifequal %} - - - + {{ book.list_html }} {% endfor %} diff --git a/apps/wiki/templates/wiki/document_list_item.html b/apps/wiki/templates/wiki/document_list_item.html new file mode 100755 index 00000000..f1c4d377 --- /dev/null +++ b/apps/wiki/templates/wiki/document_list_item.html @@ -0,0 +1,21 @@ + + + [?] + {% ifequal book.chunk_set.count 1 %} + + {{ book.title }} + {% else %} + {{ book.title }} +
+ {% for chunk in book %} + + {{ forloop.counter }}. + {{ chunk.comment }}
+ {% endfor %} +
+ {% endifequal %} + + diff --git a/apps/wiki/urls.py b/apps/wiki/urls.py index ac731eff..3e5b0675 100644 --- a/apps/wiki/urls.py +++ b/apps/wiki/urls.py @@ -47,7 +47,7 @@ urlpatterns = patterns('wiki.views', #url(r'^(?P[^/]+)/publish/(?P\d+)$', 'publish', name="wiki_publish"), url(r'^diff/(?P[^/]+)/(?:(?P[^/]+)/)?$', 'diff', name="wiki_diff"), - #url(r'^(?P[^/]+)/tags$', 'add_tag', name="wiki_add_tag"), + url(r'^tag/(?P[^/]+)/(?:(?P[^/]+)/)?$', 'add_tag', name="wiki_add_tag"), url(r'^book/(?P[^/]+)/$', 'book', name="wiki_book"), url(r'^book/(?P[^/]+)/xml$', 'book_xml', name="wiki_book_xml"), diff --git a/apps/wiki/views.py b/apps/wiki/views.py index 8fa1fc9b..60630783 100644 --- a/apps/wiki/views.py +++ b/apps/wiki/views.py @@ -17,7 +17,8 @@ from django.shortcuts import get_object_or_404, redirect from django.http import Http404 from wiki.models import Book, Chunk, Theme -from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm +from wiki.forms import (DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm, + ChunkFormSet) from datetime import datetime from django.utils.encoding import smart_unicode from django.utils.translation import ugettext_lazy as _ @@ -213,9 +214,6 @@ def text(request, slug, chunk=None): if request.method == 'POST': form = DocumentTextSaveForm(request.POST, prefix="textsave") if form.is_valid(): - # TODO: - # - stage completion should be stored (as a relation) - if request.user.is_authenticated(): author = request.user else: @@ -223,10 +221,13 @@ def text(request, slug, chunk=None): text = form.cleaned_data['text'] parent_revision = form.cleaned_data['parent_revision'] parent = doc.at_revision(parent_revision) + stage = form.cleaned_data['stage_completed'] + tags = [stage] if stage else [] doc.commit(author=author, text=text, parent=parent, description=form.cleaned_data['comment'], + tags=tags, ) revision = doc.revision() return JSONResponse({ @@ -253,8 +254,8 @@ def text(request, slug, chunk=None): @never_cache def book_xml(request, slug): - xml = get_object_or_404(Book, slug=slug).materialize() - + xml = get_object_or_404(Book, slug=slug).materialize(Book.publish_tag()) + response = http.HttpResponse(xml, content_type='application/xml', mimetype='application/wl+xml') response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug return response @@ -262,7 +263,7 @@ def book_xml(request, slug): @never_cache def book_txt(request, slug): - xml = get_object_or_404(Book, slug=slug).materialize() + xml = get_object_or_404(Book, slug=slug).materialize(Book.publish_tag()) output = StringIO() # errors? librarian.text.transform(StringIO(xml), output) @@ -274,7 +275,7 @@ def book_txt(request, slug): @never_cache def book_html(request, slug): - xml = get_object_or_404(Book, slug=slug).materialize() + xml = get_object_or_404(Book, slug=slug).materialize(Book.publish_tag()) output = StringIO() # errors? librarian.html.transform(StringIO(xml), output, parse_dublincore=False, @@ -390,7 +391,7 @@ def history(request, slug, chunk=None): "description": change.description, "author": change.author_str(), "date": change.created_at, - "tag": [], + "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()), }) return JSONResponse(changes) @@ -403,28 +404,28 @@ def book(request, slug): }) - -""" -import wlapi - - @require_POST @ajax_require_permission('wiki.can_change_tags') -def add_tag(request, name): - name = normalize_name(name) - storage = getstorage() - +def add_tag(request, slug, chunk=None): form = DocumentTagForm(request.POST, prefix="addtag") if form.is_valid(): - doc = storage.get_or_404(form.cleaned_data['id']) - doc.add_tag(tag=form.cleaned_data['tag'], - revision=form.cleaned_data['revision'], - author=request.user.username) + try: + doc = Chunk.get(slug, chunk) + except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist): + raise Http404 + + tag = form.cleaned_data['tag'] + revision = revision=form.cleaned_data['revision'] + doc.at_revision(revision).tags.add(tag) return JSONResponse({"message": _("Tag added")}) else: return JSONFormInvalid(form) +""" +import wlapi + + @require_POST @ajax_require_permission('wiki.can_publish') def publish(request, name): diff --git a/redakcja/static/js/wiki/view_history.js b/redakcja/static/js/wiki/view_history.js index 94a369b6..4fe20e25 100644 --- a/redakcja/static/js/wiki/view_history.js +++ b/redakcja/static/js/wiki/view_history.js @@ -109,9 +109,9 @@ stub: $stub, data: this, filters: { - tag: function(value) { - return tags.filter("*[value='"+value+"']").text(); - } +// tag: function(value) { +// return tags.filter("*[value='"+value+"']").text(); +// } // description: function(value) { // return value.replace('\n', '); // } diff --git a/redakcja/static/js/wiki/wikiapi.js b/redakcja/static/js/wiki/wikiapi.js index 5786f151..2f79b09b 100644 --- a/redakcja/static/js/wiki/wikiapi.js +++ b/redakcja/static/js/wiki/wikiapi.js @@ -48,7 +48,7 @@ return base_path + "/rev/" + arguments[1] + '/'; if (vname == "ajax_document_addtag") - return base_path + "/tags/" + arguments[1] + '/'; + return base_path + "/tag/" + arguments[1] + '/'; if (vname == "ajax_publish") return base_path + "/publish/" + arguments[1] + '/'; -- 2.20.1