admin.site.register(models.Book, BookAdmin)
admin.site.register(models.Chunk)
-
admin.site.register(models.Chunk.tag_model)
+
+admin.site.register(models.Image)
+admin.site.register(models.Image.tag_model)
--- /dev/null
+# 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 'ImageChange'
+ db.create_table('catalogue_imagechange', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('author', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+ ('author_name', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+ ('author_email', self.gf('django.db.models.fields.CharField')(max_length=128, null=True, blank=True)),
+ ('revision', self.gf('django.db.models.fields.IntegerField')(db_index=True)),
+ ('parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
+ ('merge_parent', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='merge_children', null=True, blank=True, to=orm['catalogue.ImageChange'])),
+ ('description', self.gf('django.db.models.fields.TextField')(default='', blank=True)),
+ ('created_at', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, db_index=True)),
+ ('publishable', self.gf('django.db.models.fields.BooleanField')(default=False)),
+ ('tree', self.gf('django.db.models.fields.related.ForeignKey')(related_name='change_set', to=orm['catalogue.Image'])),
+ ('data', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ))
+ db.send_create_signal('catalogue', ['ImageChange'])
+
+ # Adding M2M table for field tags on 'ImageChange'
+ db.create_table('catalogue_imagechange_tags', (
+ ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
+ ('imagechange', models.ForeignKey(orm['catalogue.imagechange'], null=False)),
+ ('imagetag', models.ForeignKey(orm['catalogue.imagetag'], null=False))
+ ))
+ db.create_unique('catalogue_imagechange_tags', ['imagechange_id', 'imagetag_id'])
+
+ # Adding unique constraint on 'ImageChange', fields ['tree', 'revision']
+ db.create_unique('catalogue_imagechange', ['tree_id', 'revision'])
+
+ # Adding model 'ImagePublishRecord'
+ db.create_table('catalogue_imagepublishrecord', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('image', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.Image'])),
+ ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
+ ('change', self.gf('django.db.models.fields.related.ForeignKey')(related_name='publish_log', to=orm['catalogue.ImageChange'])),
+ ))
+ db.send_create_signal('catalogue', ['ImagePublishRecord'])
+
+ # Adding model 'ImageTag'
+ db.create_table('catalogue_imagetag', (
+ ('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('catalogue', ['ImageTag'])
+
+ # Adding model 'Image'
+ db.create_table('catalogue_image', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)),
+ ('image', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
+ ('title', self.gf('django.db.models.fields.CharField')(max_length=255, blank=True)),
+ ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50, db_index=True)),
+ ('public', self.gf('django.db.models.fields.BooleanField')(default=True, db_index=True)),
+ ('_short_html', self.gf('django.db.models.fields.TextField')(null=True, blank=True)),
+ ('_new_publishable', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+ ('_published', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+ ('_changed', self.gf('django.db.models.fields.NullBooleanField')(null=True, blank=True)),
+ ('stage', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['catalogue.ImageTag'], null=True, blank=True)),
+ ('head', self.gf('django.db.models.fields.related.ForeignKey')(default=None, to=orm['catalogue.ImageChange'], null=True, blank=True)),
+ ('creator', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_image', null=True, to=orm['auth.User'])),
+ ))
+ db.send_create_signal('catalogue', ['Image'])
+
+
+ def backwards(self, orm):
+
+ # Removing unique constraint on 'ImageChange', fields ['tree', 'revision']
+ db.delete_unique('catalogue_imagechange', ['tree_id', 'revision'])
+
+ # Deleting model 'ImageChange'
+ db.delete_table('catalogue_imagechange')
+
+ # Removing M2M table for field tags on 'ImageChange'
+ db.delete_table('catalogue_imagechange_tags')
+
+ # Deleting model 'ImagePublishRecord'
+ db.delete_table('catalogue_imagepublishrecord')
+
+ # Deleting model 'ImageTag'
+ db.delete_table('catalogue_imagetag')
+
+ # Deleting model 'Image'
+ db.delete_table('catalogue_image')
+
+
+ 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', 'content_type__model', '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'})
+ },
+ 'catalogue.book': {
+ 'Meta': {'ordering': "['title', 'slug']", 'object_name': 'Book'},
+ '_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'}),
+ '_single': ('django.db.models.fields.NullBooleanField', [], {'db_index': 'True', 'null': 'True', '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'}),
+ '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'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '128', 'db_index': 'True'}),
+ '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']"}),
+ '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': "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': "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'}),
+ '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'}),
+ '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': "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': "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'}),
+ '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']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'catalogue.chunktag': {
+ 'Meta': {'ordering': "['ordering']", 'object_name': 'ChunkTag'},
+ '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'})
+ },
+ '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': "orm['auth.User']"}),
+ 'head': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['catalogue.ImageChange']", 'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
+ 'public': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
+ 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50', 'db_index': 'True'}),
+ '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': "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': "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'}),
+ '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']"}),
+ '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': "orm['auth.User']"})
+ },
+ 'catalogue.imagetag': {
+ 'Meta': {'ordering': "['ordering']", 'object_name': 'ImageTag'},
+ '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'})
+ },
+ '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'})
+ }
+ }
+
+ complete_apps = ['catalogue']
# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
#
from catalogue.models.chunk import Chunk
+from catalogue.models.image import Image
from catalogue.models.publish_log import BookPublishRecord, ChunkPublishRecord
from catalogue.models.book import Book
from catalogue.models.listeners import *
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django.conf import settings
+from django.db import models
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext_lazy as _
+from catalogue.helpers import cached_in_field
+from catalogue.tasks import refresh_instance
+from dvcs import models as dvcs_models
+
+
+class Image(dvcs_models.Document):
+ """ An editable chunk of text. Every Book text is divided into chunks. """
+ REPO_PATH = settings.CATALOGUE_IMAGE_REPO_PATH
+
+ image = models.FileField(_('image'), upload_to='catalogue/images')
+ title = models.CharField(_('title'), max_length=255, blank=True)
+ slug = models.SlugField(_('slug'), unique=True)
+ public = models.BooleanField(_('public'), default=True, db_index=True)
+
+ # cache
+ _short_html = models.TextField(null=True, blank=True, editable=False)
+ _new_publishable = models.NullBooleanField(editable=False)
+ _published = models.NullBooleanField(editable=False)
+ _changed = models.NullBooleanField(editable=False)
+
+ class Meta:
+ app_label = 'catalogue'
+ ordering = ['title']
+ verbose_name = _('image')
+ verbose_name_plural = _('images')
+ permissions = [('can_pubmark_image', 'Can mark images for publishing')]
+
+ # Representing
+ # ============
+
+ def __unicode__(self):
+ return self.title
+
+ @models.permalink
+ def get_absolute_url(self):
+ return ("wiki_img_editor", [self.slug])
+
+ # State & cache
+ # =============
+
+ def accessible(self, request):
+ return self.public or request.user.is_authenticated()
+
+ def is_new_publishable(self):
+ change = self.publishable()
+ if not change:
+ return False
+ return change.publish_log.exists()
+ new_publishable = cached_in_field('_new_publishable')(is_new_publishable)
+
+ def is_published(self):
+ return self.publish_log.exists()
+ published = cached_in_field('_published')(is_published)
+
+ def is_changed(self):
+ if self.head is None:
+ return False
+ return not self.head.publishable
+ changed = cached_in_field('_changed')(is_changed)
+
+ @cached_in_field('_short_html')
+ def short_html(self):
+ return render_to_string(
+ 'catalogue/image_short.html', {'image': self})
+
+ def refresh(self):
+ """This should be done offline."""
+ self.short_html
+ self.single
+ self.new_publishable
+ self.published
+
+ def touch(self):
+ update = {
+ "_changed": self.is_changed(),
+ "_short_html": None,
+ "_new_publishable": self.is_new_publishable(),
+ "_published": self.is_published(),
+ }
+ Image.objects.filter(pk=self.pk).update(**update)
+ refresh_instance(self)
+
+ def refresh(self):
+ """This should be done offline."""
+ self.changed
+ self.short_html
#
from django.contrib.auth.models import User
from django.db import models
-from catalogue.models import Book, Chunk
+from catalogue.models import Book, Chunk, Image
from catalogue.signals import post_publish
from dvcs.signals import post_publishable
models.signals.post_save.connect(chunk_changed, sender=Chunk)
+def image_changed(sender, instance, created, **kwargs):
+ instance.touch()
+models.signals.post_save.connect(image_changed, sender=Image)
+
+
def user_changed(sender, instance, *args, **kwargs):
books = set()
for c in instance.chunk_set.all():
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import ugettext_lazy as _
-from catalogue.models import Chunk
+from catalogue.models import Chunk, Image
class BookPublishRecord(models.Model):
app_label = 'catalogue'
verbose_name = _('chunk publish record')
verbose_name = _('chunk publish records')
+
+
+class ImagePublishRecord(models.Model):
+ """A record left after publishing an Image."""
+
+ image = models.ForeignKey(Image, verbose_name=_('image'), related_name='publish_log')
+ timestamp = models.DateTimeField(_('time'), auto_now_add=True)
+ user = models.ForeignKey(User, verbose_name=_('user'))
+ change = models.ForeignKey(Image.change_model, related_name='publish_log', verbose_name=_('change'))
+
+ class Meta:
+ app_label = 'catalogue'
+ ordering = ['-timestamp']
+ verbose_name = _('image publish record')
+ verbose_name = _('image publish records')
--- /dev/null
+{% load i18n %}
+{% load compressed %}
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+ <title>{{ book.title }}</title>
+ </head>
+ <body>
+ <div id="menu">
+ <ul>
+ <li><a href="#toc">{% trans "Table of contents" %}</a></li>
+ <li><a href="#nota_red">{% trans "Edit. note" %}</a></li>
+ <li><a href="#info">{% trans "Infobox" %}</a></li>
+ </ul>
+ </div>
+ <div id="info">
+ {#% book_info book %#}
+ </div>
+ <div id="header">
+ <div id="logo">
+ <a href="/"><img src="http://static.wolnelektury.pl/img/logo.png" alt="WolneLektury.pl - logo" /></a>
+ </div>
+ </div>
+
+ {{ html|safe }}
+
+ </body>
+</html>
--- /dev/null
+{% extends "catalogue/base.html" %}
+
+{% load i18n %}
+{% load catalogue book_list %}
+
+
+{% block content %}
+ {% image_list %}
+{% endblock content %}
--- /dev/null
+{% load i18n %}
+
+<tr>
+ <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.title }}</a></td>
+ <td>{% if image.stage %}
+ {{ 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>
+ {% if image.published %}P{% endif %}
+ {% if image.new_publishable %}p{% endif %}
+ {% if image.changed %}+{% endif %}
+ </td>
+</tr>
--- /dev/null
+{% load i18n %}
+{% load pagination_tags %}
+
+
+<form name='filter' action=''>
+<input type='hidden' name="title" value="{{ request.GET.title }}" />
+<input type='hidden' name="stage" value="{{ request.GET.stage }}" />
+{% if not viewed_user %}
+ <input type='hidden' name="user" value="{{ request.GET.user }}" />
+{% endif %}
+<input type='hidden' name="status" value="{{ request.GET.status }}" />
+</form>
+
+<table id="file-list"{% if viewed_user %} class="book-list-user"{% endif %}>
+ <thead><tr>
+ <th></th>
+ <th class='book-search-column'>
+ <form>
+ <input title='{% trans "Search in book titles" %}' name="title"
+ class='text-filter' value="{{ request.GET.title }}" />
+ </form>
+ </th>
+ <th><select name="stage" class="filter">
+ <option value=''>- {% trans "stage" %} -</option>
+ <option {% if request.GET.stage == '-' %}selected="selected"
+ {% endif %}value="-">- {% trans "none" %} -</option>
+ {% for stage in stages %}
+ <option {% if request.GET.stage == stage.slug %}selected="selected"
+ {% endif %}value="{{ stage.slug }}">{{ stage.name }}</option>
+ {% endfor %}
+ </select></th>
+
+ {% if not viewed_user %}
+ <th><select name="user" class="filter">
+ <option value=''>- {% trans "editor" %} -</option>
+ <option {% if request.GET.user == '-' %}selected="selected"
+ {% 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>
+ {% endfor %}
+ </select></th>
+ {% endif %}
+
+ <th><select name="status" class="filter">
+ <option value=''>- {% trans "status" %} -</option>
+ {% for state, label in states %}
+ <option {% if request.GET.status == state %}selected="selected"
+ {% endif %}value='{{ state }}'>{{ label }}</option>
+ {% endfor %}
+ </select></th>
+
+ </tr></thead>
+
+ {% with cnt=objects|length %}
+ {% autopaginate objects 100 %}
+ <tbody>
+ {% for item in objects %}
+ {{ item.short_html|safe }}
+ {% endfor %}
+ <tr><th class='paginator' colspan="5">
+ {% paginate %}
+ {% blocktrans count c=cnt %}{{c}} image{% plural %}{{c}} images{% endblocktrans %}</th></tr>
+ </tbody>
+ {% endwith %}
+</table>
+{% if not objects %}
+ <p>{% trans "No images found." %}</p>
+{% endif %}
--- /dev/null
+{% extends "catalogue/base.html" %}
+{% load i18n %}
+
+
+{% block content %}
+
+
+<h2>{% trans "PDF file upload" %}</h2>
+
+<form enctype="multipart/form-data" method="POST" action="">
+{% csrf_token %}
+{{ form.as_p }}
+<p><button type="submit">{% trans "Upload" %}</button></p>
+</form>
+
+
+{% endblock content %}
from django import template
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
-from catalogue.models import Chunk
+from catalogue.models import Chunk, Image
register = template.Library()
})
return new_context
+
+
+
+_image_states = [
+ ('publishable', _('publishable'), Q(_new_publishable=True)),
+ ('changed', _('changed'), Q(_changed=True)),
+ ('published', _('published'), Q(_published=True)),
+ ('unpublished', _('unpublished'), Q(_published=False)),
+ ('empty', _('empty'), Q(head=None)),
+ ]
+_image_states_options = [s[:2] for s in _states]
+_image_states_dict = dict([(s[0], s[2]) for s in _states])
+
+def image_list_filter(request, **kwargs):
+
+ def arg_or_GET(field):
+ return kwargs.get(field, request.GET.get(field))
+
+ images = Image.objects.all()
+
+ if not request.user.is_authenticated():
+ images = images.filter(public=True)
+
+ state = arg_or_GET('status')
+ if state in _image_states_dict:
+ images = images.filter(_image_states_dict[state])
+
+ images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username')
+ images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug')
+ images = search_filter(images, arg_or_GET('title'), ['title', 'title'])
+ return images
+
+
+@register.inclusion_tag('catalogue/image_table.html', takes_context=True)
+def image_list(context, user=None):
+ request = context['request']
+
+ if user:
+ filters = {"user": user}
+ new_context = {"viewed_user": user}
+ else:
+ filters = {}
+ new_context = {"users": User.objects.annotate(
+ count=Count('chunk')).filter(count__gt=0).order_by(
+ '-count', 'last_name', 'first_name')}
+
+ new_context.update({
+ "filters": True,
+ "request": request,
+ "objects": image_list_filter(request, **filters),
+ "stages": Image.tag_model.objects.all(),
+ "states": _image_states_options,
+ })
+
+ return new_context
tabs.append(Tab('activity', _('Activity'), reverse("catalogue_activity")))
tabs.append(Tab('all', _('All'), reverse("catalogue_document_list")))
+ tabs.append(Tab('images', _('Images'), reverse("catalogue_image_list")))
tabs.append(Tab('users', _('Users'), reverse("catalogue_users")))
if user.has_perm('catalogue.add_book'):
urlpatterns = patterns('catalogue.views',
url(r'^$', redirect_to, {'url': 'catalogue/'}),
+ url(r'^images/$', 'image_list', name='catalogue_image_list'),
+ url(r'^image/(?P<slug>[^/]+)/$', 'image', name="catalogue_image"),
+
url(r'^catalogue/$', 'document_list', name='catalogue_document_list'),
url(r'^user/$', 'my', name='catalogue_user'),
url(r'^user/(?P<username>[^/]+)/$', 'user', name='catalogue_user'),
return render(request, 'catalogue/document_list.html')
+@active_tab('images')
+@never_cache
+def image_list(request, user=None):
+ return render(request, 'catalogue/image_list.html')
+
+
@never_cache
def user(request, username):
user = get_object_or_404(User, username=username)
})
+def image(request, slug):
+ image = get_object_or_404(Image, slug=slug)
+ if not image.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
+ if request.user.has_perm('catalogue.change_image'):
+ if request.method == "POST":
+ form = forms.ImageForm(request.POST, instance=image)
+ if form.is_valid():
+ form.save()
+ return http.HttpResponseRedirect(image.get_absolute_url())
+ else:
+ form = forms.ImageForm(instance=image)
+ editable = True
+ else:
+ form = forms.ReadonlyImageForm(instance=image)
+ editable = False
+
+ #publish_error = publishable_error(book)
+ publish_error = 'Publishing not implemented yet.'
+ publishable = publish_error is None
+
+ return direct_to_template(request, "catalogue/image_detail.html", extra_context={
+ "object": image,
+ "publishable": publishable,
+ "publishable_error": publish_error,
+ "form": form,
+ "editable": editable,
+ })
+
+
@permission_required('catalogue.add_chunk')
def chunk_add(request, slug, chunk):
try:
--- /dev/null
+from django.test import TestCase
+from dvcs.models import Change, Document
+from django.contrib.auth.models import User
+
+class DocumentModelTests(TestCase):
+
+ def setUp(self):
+ self.user = User.objects.create_user("tester", "tester@localhost.local")
+
+ def assertTextEqual(self, given, expected):
+ return self.assertEqual(given, expected,
+ "Expected '''%s'''\n differs from text: '''%s'''" % (expected, given)
+ )
+
+ def test_empty_file(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ self.assert_(doc.head is not None)
+ self.assertEqual(doc.materialize(), u"")
+
+ def test_single_commit(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ doc.commit(text=u"Ala ma kota", description="Commit #1", author=self.user)
+ self.assert_(doc.head is not None)
+ self.assertEqual(doc.change_set.count(), 2)
+ self.assertEqual(doc.materialize(), u"Ala ma kota")
+
+ def test_chained_commits(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ c1 = doc.commit(description="Commit #1", text=u"""
+ Line #1
+ Line #2 is cool
+ """, author=self.user)
+ c2 = doc.commit(description="Commit #2", text=u"""
+ Line #1
+ Line #2 is hot
+ """, author=self.user)
+ c3 = doc.commit(description="Commit #3", text=u"""
+ Line #1
+ ... is hot
+ Line #3 ate Line #2
+ """, author=self.user)
+ self.assert_(doc.head is not None)
+ self.assertEqual(doc.change_set.count(), 4)
+
+ self.assertEqual(doc.materialize(), u"""
+ Line #1
+ ... is hot
+ Line #3 ate Line #2
+ """)
+ self.assertEqual(doc.materialize(version=c3), u"""
+ Line #1
+ ... is hot
+ Line #3 ate Line #2
+ """)
+ self.assertEqual(doc.materialize(version=c2), u"""
+ Line #1
+ Line #2 is hot
+ """)
+ self.assertEqual(doc.materialize(version=c1), """
+ Line #1
+ Line #2 is cool
+ """)
+
+
+ def test_parallel_commit_noconflict(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ self.assert_(doc.head is not None)
+ base = doc.head
+ base = doc.commit(description="Commit #1", text=u"""
+ Line #1
+ Line #2
+""", author=self.user)
+
+ c1 = doc.commit(description="Commit #2", text=u"""
+ Line #1 is hot
+ Line #2
+""", parent=base, author=self.user)
+ self.assertTextEqual(c1.materialize(), u"""
+ Line #1 is hot
+ Line #2
+""")
+ c2 = doc.commit(description="Commit #3", text=u"""
+ Line #1
+ Line #2
+ Line #3
+""", parent=base, author=self.user)
+ self.assertEqual(doc.change_set.count(), 5)
+ self.assertTextEqual(doc.materialize(), u"""
+ Line #1 is hot
+ Line #2
+ Line #3
+""")
+
+ def test_parallel_commit_conflict(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ self.assert_(doc.head is not None)
+ base = doc.head
+ base = doc.commit(description="Commit #1", text=u"""
+Line #1
+Line #2
+Line #3
+""", author=self.user)
+
+ c1 = doc.commit(description="Commit #2", text=u"""
+Line #1
+Line #2 is hot
+Line #3
+""", parent=base, author=self.user)
+ c2 = doc.commit(description="Commit #3", text=u"""
+Line #1
+Line #2 is cool
+Line #3
+""", parent=base, author=self.user)
+ self.assertEqual(doc.change_set.count(), 5)
+ self.assertTextEqual(doc.materialize(), u"""
+Line #1
+<<<<<<<
+Line #2 is hot
+=======
+Line #2 is cool
+>>>>>>>
+Line #3
+""")
+
+ def test_multiply_parallel_commits(self):
+ doc = Document.objects.create(name=u"Sample Document", creator=self.user)
+ self.assert_(doc.head is not None)
+ c1 = doc.commit(description="Commit A1", text=u"""
+Line #1
+
+Line #2
+
+Line #3
+""", author=self.user)
+ c2 = doc.commit(description="Commit A2", text=u"""
+Line #1 *
+
+Line #2
+
+Line #3
+""", author=self.user)
+ c3 = doc.commit(description="Commit B1", text=u"""
+Line #1
+
+Line #2 **
+
+Line #3
+""", parent=c1, author=self.user)
+ c4 = doc.commit(description="Commit C1", text=u"""
+Line #1 *
+
+Line #2
+
+Line #3 ***
+""", parent=c2, author=self.user)
+ self.assertEqual(doc.change_set.count(), 7)
+ self.assertTextEqual(doc.materialize(), u"""
+Line #1 *
+
+Line #2 **
+
+Line #3 ***
+""")
+
--- /dev/null
+# -*- coding: utf-8
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('dvcs.views',
+ url(r'^data/(?P<document_id>[^/]+)/(?P<version>.*)$', 'document_data', name='storage_document_data'),
+)
--- /dev/null
+# Create your views here.
+from django.views.generic.simple import direct_to_template
+from django import http
+from dvcs.models import Document
+
+def document_list(request, template_name="dvcs/document_list.html"):
+ return direct_to_template(request, template_name, {
+ "documents": Document.objects.all(),
+ })
+
+def document_data(request, document_id, version=None):
+ doc = Document.objects.get(pk=document_id)
+ return http.HttpResponse(doc.materialize(version or None), content_type="text/plain")
+
+def document_history(request, docid, template_name="dvcs/document_history.html"):
+ document = Document.objects.get(pk=docid)
+ return direct_to_template(request, template_name, {
+ "document": document,
+ "changes": document.history(),
+ })
+
list_editable = ('label', 'tooltip', 'accesskey')
prepopulated_fields = {'slug': ('label',)}
-admin.site.register(models.Button, ButtonAdmin)
-admin.site.register(models.ButtonGroup)
-admin.site.register(models.Scriptlet)
+#admin.site.register(models.Button, ButtonAdmin)
+#admin.site.register(models.ButtonGroup)
+#admin.site.register(models.Scriptlet)
from wiki import models
-
class ThemeAdmin(admin.ModelAdmin):
search_fields = ['name']
--- /dev/null
+ # pragma: no cover
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
+from django import forms
+from django.utils.translation import ugettext_lazy as _
+from wiki.forms import DocumentTextSaveForm
+from catalogue.models import Image
+
+
+class ImageSaveForm(DocumentTextSaveForm):
+ """Form for saving document's text."""
+
+ stage_completed = forms.ModelChoiceField(
+ queryset=Image.tag_model.objects.all(),
+ required=False,
+ label=_(u"Completed"),
+ help_text=_(u"If you completed a life cycle stage, select it."),
+ )
--- /dev/null
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Platforma Redakcyjna\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-09-29 15:34+0200\n"
+"PO-Revision-Date: 2010-09-29 15:36+0100\n"
+"Last-Translator: Radek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>\n"
+"Language-Team: Fundacja Nowoczesna Polska <fundacja@nowoczesnapolska.org.pl>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: constants.py:6
+msgid "First correction"
+msgstr "Autokorekta"
+
+#: constants.py:7
+msgid "Tagging"
+msgstr "Tagowanie"
+
+#: constants.py:8
+msgid "Initial Proofreading"
+msgstr "Korekta"
+
+#: constants.py:9
+msgid "Annotation Proofreading"
+msgstr "Sprawdzenie przypisów źródła"
+
+#: constants.py:10
+msgid "Modernisation"
+msgstr "Uwspółcześnienie"
+
+#: constants.py:11
+#: templates/wiki/tabs/annotations_view_item.html:3
+msgid "Annotations"
+msgstr "Przypisy"
+
+#: constants.py:12
+msgid "Themes"
+msgstr "Motywy"
+
+#: constants.py:13
+msgid "Editor's Proofreading"
+msgstr "Ostateczna redakcja literacka"
+
+#: constants.py:14
+msgid "Technical Editor's Proofreading"
+msgstr "Ostateczna redakcja techniczna"
+
+#: constants.py:18
+msgid "Ready to publish"
+msgstr "Gotowe do publikacji"
+
+#: forms.py:49
+msgid "ZIP file"
+msgstr "Plik ZIP"
+
+#: forms.py:82
+msgid "Author"
+msgstr "Autor"
+
+#: forms.py:83
+msgid "Your name"
+msgstr "Imię i nazwisko"
+
+#: forms.py:88
+msgid "Author's email"
+msgstr "E-mail autora"
+
+#: forms.py:89
+msgid "Your email address, so we can show a gravatar :)"
+msgstr "Adres e-mail, żebyśmy mogli pokazać gravatar :)"
+
+#: forms.py:95
+msgid "Your comments"
+msgstr "Twój komentarz"
+
+#: forms.py:96
+msgid "Describe changes you made."
+msgstr "Opisz swoje zmiany"
+
+#: forms.py:102
+msgid "Completed"
+msgstr "Ukończono"
+
+#: forms.py:103
+msgid "If you completed a life cycle stage, select it."
+msgstr "Jeśli został ukończony etap prac, wskaż go."
+
+#: models.py:93
+#, python-format
+msgid "Finished stage: %s"
+msgstr "Ukończony etap: %s"
+
+#: models.py:152
+msgid "name"
+msgstr "nazwa"
+
+#: models.py:156
+msgid "theme"
+msgstr "motyw"
+
+#: models.py:157
+msgid "themes"
+msgstr "motywy"
+
+#: views.py:167
+#, python-format
+msgid "Title already used for %s"
+msgstr "Nazwa taka sama jak dla pliku %s"
+
+#: views.py:169
+msgid "Title already used in repository."
+msgstr "Plik o tej nazwie już istnieje w repozytorium."
+
+#: views.py:175
+msgid "File should be UTF-8 encoded."
+msgstr "Plik powinien mieć kodowanie UTF-8."
+
+#: views.py:358
+msgid "Tag added"
+msgstr "Dodano tag"
+
+#: templates/wiki/base.html:15
+msgid "Platforma Redakcyjna"
+msgstr ""
+
+#: templates/wiki/diff_table.html:5
+msgid "Old version"
+msgstr "Stara wersja"
+
+#: templates/wiki/diff_table.html:6
+msgid "New version"
+msgstr "Nowa wersja"
+
+#: templates/wiki/document_create_missing.html:8
+msgid "Create document"
+msgstr "Utwórz dokument"
+
+#: templates/wiki/document_details.html:32
+msgid "Click to open/close gallery"
+msgstr "Kliknij, aby (ro)zwinąć galerię"
+
+#: templates/wiki/document_details_base.html:36
+msgid "Help"
+msgstr "Pomoc"
+
+#: templates/wiki/document_details_base.html:38
+msgid "Version"
+msgstr "Wersja"
+
+#: templates/wiki/document_details_base.html:38
+msgid "Unknown"
+msgstr "nieznana"
+
+#: templates/wiki/document_details_base.html:40
+#: templates/wiki/tag_dialog.html:15
+msgid "Save"
+msgstr "Zapisz"
+
+#: templates/wiki/document_details_base.html:41
+msgid "Save attempt in progress"
+msgstr "Trwa zapisywanie"
+
+#: templates/wiki/document_details_base.html:42
+msgid "There is a newer version of this document!"
+msgstr "Istnieje nowsza wersja tego dokumentu!"
+
+#: templates/wiki/document_list.html:30
+msgid "Clear filter"
+msgstr "Wyczyść filtr"
+
+#: templates/wiki/document_list.html:48
+msgid "Your last edited documents"
+msgstr "Twoje ostatnie edycje"
+
+#: templates/wiki/document_upload.html:9
+msgid "Bulk documents upload"
+msgstr "Hurtowe dodawanie dokumentów"
+
+#: templates/wiki/document_upload.html:12
+msgid "Please submit a ZIP with UTF-8 encoded XML files. Files not ending with <code>.xml</code> will be ignored."
+msgstr "Proszę wskazać archiwum ZIP z plikami XML w kodowaniu UTF-8. Pliki nie kończące się na <code>.xml</code> zostaną zignorowane."
+
+#: templates/wiki/document_upload.html:17
+msgid "Upload"
+msgstr "Dodaj"
+
+#: templates/wiki/document_upload.html:24
+msgid "There have been some errors. No files have been added to the repository."
+msgstr "Wystąpiły błędy. Żadne pliki nie zostały dodane do repozytorium."
+
+#: templates/wiki/document_upload.html:25
+msgid "Offending files"
+msgstr "Błędne pliki"
+
+#: templates/wiki/document_upload.html:33
+msgid "Correct files"
+msgstr "Poprawne pliki"
+
+#: templates/wiki/document_upload.html:44
+msgid "Files have been successfully uploaded to the repository."
+msgstr "Pliki zostały dodane do repozytorium."
+
+#: templates/wiki/document_upload.html:45
+msgid "Uploaded files"
+msgstr "Dodane pliki"
+
+#: templates/wiki/document_upload.html:55
+msgid "Skipped files"
+msgstr "Pominięte pliki"
+
+#: templates/wiki/document_upload.html:56
+msgid "Files skipped due to no <code>.xml</code> extension"
+msgstr "Pliki pominięte z powodu braku rozszerzenia <code>.xml</code>."
+
+#: templates/wiki/tag_dialog.html:16
+msgid "Cancel"
+msgstr "Anuluj"
+
+#: templates/wiki/tabs/annotations_view.html:5
+msgid "Refresh"
+msgstr "Odśwież"
+
+#: templates/wiki/tabs/gallery_view.html:7
+msgid "Previous"
+msgstr "Poprzednie"
+
+#: templates/wiki/tabs/gallery_view.html:13
+msgid "Next"
+msgstr "Następne"
+
+#: templates/wiki/tabs/gallery_view.html:15
+msgid "Zoom in"
+msgstr "Powiększ"
+
+#: templates/wiki/tabs/gallery_view.html:16
+msgid "Zoom out"
+msgstr "Zmniejsz"
+
+#: templates/wiki/tabs/gallery_view_item.html:3
+msgid "Gallery"
+msgstr "Galeria"
+
+#: templates/wiki/tabs/history_view.html:5
+msgid "Compare versions"
+msgstr "Porównaj wersje"
+
+#: templates/wiki/tabs/history_view.html:7
+msgid "Mark version"
+msgstr "Oznacz wersję"
+
+#: templates/wiki/tabs/history_view.html:9
+msgid "Revert document"
+msgstr "Przywróć wersję"
+
+#: templates/wiki/tabs/history_view.html:12
+msgid "View version"
+msgstr "Zobacz wersję"
+
+#: templates/wiki/tabs/history_view_item.html:3
+msgid "History"
+msgstr "Historia"
+
+#: templates/wiki/tabs/search_view.html:3
+#: templates/wiki/tabs/search_view.html:5
+msgid "Search"
+msgstr "Szukaj"
+
+#: templates/wiki/tabs/search_view.html:8
+msgid "Replace with"
+msgstr "Zamień na"
+
+#: templates/wiki/tabs/search_view.html:10
+msgid "Replace"
+msgstr "Zamień"
+
+#: templates/wiki/tabs/search_view.html:13
+msgid "Options"
+msgstr "Opcje"
+
+#: templates/wiki/tabs/search_view.html:15
+msgid "Case sensitive"
+msgstr "Rozróżniaj wielkość liter"
+
+#: templates/wiki/tabs/search_view.html:17
+msgid "From cursor"
+msgstr "Zacznij od kursora"
+
+#: templates/wiki/tabs/search_view_item.html:3
+msgid "Search and replace"
+msgstr "Znajdź i zamień"
+
+#: templates/wiki/tabs/source_editor_item.html:5
+msgid "Source code"
+msgstr "Kod źródłowy"
+
+#: templates/wiki/tabs/summary_view.html:10
+msgid "Title"
+msgstr "Tytuł"
+
+#: templates/wiki/tabs/summary_view.html:15
+msgid "Document ID"
+msgstr "ID dokumentu"
+
+#: templates/wiki/tabs/summary_view.html:19
+msgid "Current version"
+msgstr "Aktualna wersja"
+
+#: templates/wiki/tabs/summary_view.html:22
+msgid "Last edited by"
+msgstr "Ostatnio edytowane przez"
+
+#: templates/wiki/tabs/summary_view.html:26
+msgid "Link to gallery"
+msgstr "Link do galerii"
+
+#: templates/wiki/tabs/summary_view.html:31
+msgid "Publish"
+msgstr "Opublikuj"
+
+#: templates/wiki/tabs/summary_view_item.html:4
+msgid "Summary"
+msgstr "Podsumowanie"
+
+#: templates/wiki/tabs/wysiwyg_editor.html:9
+msgid "Insert theme"
+msgstr "Wstaw motyw"
+
+#: templates/wiki/tabs/wysiwyg_editor.html:12
+msgid "Insert annotation"
+msgstr "Wstaw przypis"
+
+#: templates/wiki/tabs/wysiwyg_editor.html:15
+msgid "Insert special character"
+msgstr "Wstaw znak specjalny"
+
+#: templates/wiki/tabs/wysiwyg_editor_item.html:3
+msgid "Visual editor"
+msgstr "Edytor wizualny"
+
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
+# Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
+#
--- /dev/null
+{% extends "base.html" %}
+{% load compressed i18n %}
+
+{% block title %}{{ document_name }} - {{ block.super }}{% endblock %}
+
+{% block extrahead %}
+{% compressed_css 'listing' %}
+{% endblock %}
+
+{% block extrabody %}
+{% compressed_js 'listing' %}
+{% endblock %}
+
+{% block maincontent %}
+<h1><img src="{{ STATIC_URL }}img/logo.png">{% trans "Platforma Redakcyjna" %}</h1>
+<div id="wiki_layout_left_column">
+ {% block leftcolumn %}
+ {% endblock leftcolumn %}
+</div>
+<div id="wiki_layout_right_column">
+ {% block rightcolumn %}
+ {% endblock rightcolumn %}
+</div>
+{% endblock maincontent %}
\ No newline at end of file
--- /dev/null
+{% load i18n %}
+<table class="diff_table">
+ <thead>
+ <tr>
+ <th colspan="2">{% trans "Old version" %}</th>
+ <th colspan="2">{% trans "New version" %}</th>
+ </tr>
+ </thead>
+<tbody>
+{% for an, a, bn, b, has_change in changes %}
+
+<tr class="{% if has_change %}change{% endif %}">
+<td>{{an}}</td>
+<td class="left">{{ a|safe }} </td>
+<td>{{bn}}</td>
+<td class="right">{{ b|safe }} </td>
+</tr>
+
+{% endfor %}
+</tbody>
+</table>
\ No newline at end of file
--- /dev/null
+{% extends "wiki_img/document_details_base.html" %}
+{% load i18n %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{ STATIC_URL }}js/wiki_img/loader.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+ {% include "wiki_img/tabs/summary_view_item.html" %}
+ {% include "wiki_img/tabs/motifs_editor_item.html" %}
+ {% include "wiki_img/tabs/objects_editor_item.html" %}
+ {% include "wiki_img/tabs/source_editor_item.html" %}
+ {% include "wiki/tabs/history_view_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+ {% include "wiki_img/tabs/summary_view.html" %}
+ {% include "wiki_img/tabs/motifs_editor.html" %}
+ {% include "wiki_img/tabs/objects_editor.html" %}
+ {% include "wiki_img/tabs/source_editor.html" %}
+ {% include "wiki_img/tabs/history_view.html" %}
+{% endblock %}
+
+{% block dialogs %}
+ {% include "wiki_img/save_dialog.html" %}
+{% endblock %}
+
+{% block editor-class %}
+ sideless
+{% endblock %}
+
--- /dev/null
+{% extends "base.html" %}
+{% load toolbar_tags i18n %}
+
+{% block title %}{{ document.name }} - {{ block.super }}{% endblock %}
+{% block extrahead %}
+{% load compressed %}
+{% compressed_css 'detail' %}
+{% endblock %}
+
+{% block extrabody %}
+<script type="text/javascript" charset="utf-8">
+ var STATIC_URL = '{{STATIC_URL}}';
+</script>
+{% compressed_js 'wiki_img' %}
+{% endblock %}
+
+{% block maincontent %}
+<div id="document-meta"
+ data-object-id="{{ document.pk }}" style="display:none">
+
+ <span data-key="revision">{{ revision }}</span>
+ <span data-key="diff">{{ request.GET.diff }}</span>
+
+ {% block meta-extra %} {% endblock %}
+</div>
+
+<div id="header">
+ <h1><a href="{% url catalogue_document_list %}"><img src="{{STATIC_URL}}icons/go-home.png"/><a href="{% url catalogue_document_list %}">Strona<br>główna</a></h1>
+ <div id="tools">
+ <a href="{{ REDMINE_URL }}projects/wl-publikacje/wiki/Pomoc" target="_blank">
+ {% trans "Help" %}</a>
+ | {% include "registration/head_login.html" %}
+ | {% trans "Version" %}: <span id="document-revision">{% trans "Unknown" %}</span>
+ {% if not readonly %}
+ | <button style="margin-left: 6px" id="save-button">{% trans "Save" %}</button>
+ <span id='save-attempt-info'>{% trans "Save attempt in progress" %}</span>
+ <span id='out-of-date-info'>{% trans "There is a newer version of this document!" %}</span>
+ {% endif %}
+ </div>
+ <ol id="tabs" class="tabs">
+ {% block tabs-menu %} {% endblock %}
+ </ol>
+</div>
+<div id="splitter">
+ <div id="editor" class="{% block editor-class %} {% endblock %}">
+ {% block tabs-content %} {% endblock %}
+ </div>
+</div>
+
+{% block dialogs %} {% endblock %}
+
+{% endblock %}
--- /dev/null
+{% extends "wiki_img/document_details_base.html" %}
+{% load i18n %}
+
+{% block extrabody %}
+{{ block.super }}
+<script src="{{ STATIC_URL }}js/lib/codemirror-0.8/codemirror.js" type="text/javascript" charset="utf-8">
+</script>
+<script src="{{ STATIC_URL }}js/wiki_img/loader_readonly.js" type="text/javascript" charset="utf-8"> </script>
+{% endblock %}
+
+{% block tabs-menu %}
+ {% include "wiki_img/tabs/motifs_editor_item.html" %}
+ {% include "wiki_img/tabs/objects_editor_item.html" %}
+ {% include "wiki_img/tabs/source_editor_item.html" %}
+{% endblock %}
+
+{% block tabs-content %}
+ {% include "wiki_img/tabs/motifs_editor.html" %}
+ {% include "wiki_img/tabs/objects_editor.html" %}
+ {% include "wiki_img/tabs/source_editor.html" %}
+{% endblock %}
+
+{% block editor-class %}
+ sideless
+{% endblock %}
+
--- /dev/null
+{% load i18n %}
+<div id="save_dialog" class="dialog" data-ui-jsclass="SaveDialog">
+ <form method="POST" action="">
+ {% csrf_token %}
+ <p>{{ forms.text_save.comment.label }}</p>
+ <p class="help_text">
+ {{ forms.text_save.comment.help_text}}
+ <span data-ui-error-for="{{ forms.text_save.comment.name }}"> </span>
+ </p>
+ {{forms.text_save.comment }}
+
+
+
+ {% for f in forms.text_save.hidden_fields %}
+ {{ f }}
+ {% endfor %}
+
+ <p data-ui-error-for="__all__"> </p>
+
+ <p class="action_area">
+ <button type="submit" class"ok" data-ui-action="save">Zapisz</button>
+ <button type="button" class="cancel" data-ui-action="cancel">Anuluj</button>
+ </p>
+ </form>
+</div>
--- /dev/null
+{% load i18n %}
+<div id="history-view-editor" class="editor" style="display: none">
+ <div class="toolbar">
+ <button type="button" id="make-diff-button"
+ data-enabled-when="2" disabled="disabled">{% trans "Compare versions" %}</button>
+ {% if can_pubmark %}
+ <button type="button" id="pubmark-changeset-button"
+ data-enabled-when="1" disabled="disabled">{% trans "Mark for publishing" %}</button>
+ {% endif %}
+ <button type="button" id="doc-revert-button"
+ 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_img_editor_readonly document.slug %}">{% trans "View version" %}</button>
+
+ </div>
+ <div id="history-view">
+ <p class="message-box" style="display:none;"></p>
+
+ <table id="changes-list-container">
+ <tbody id="changes-list">
+ </tbody>
+ <tbody style="display: none;">
+ <tr class="entry row-stub">
+ <td data-stub-value="version"></td>
+ <td>
+ <span data-stub-value="date"></span>
+ <br/><span data-stub-value="author"></span>
+ <br />
+ <span data-stub-value="description"></span>
+ </td>
+ <td>
+ <div data-stub-value="publishable"></div>
+ <div data-stub-value="tag"></div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+</div>
--- /dev/null
+{% load i18n %}
+<div id="motifs-editor" class="editor" style="display: none">
+ <div class="toolbar">
+ <input id='tag-name' title='{% trans "Motifs" %}' />
+ <button id='add'>{% trans "Add" %}</button>
+
+ <span id="objects-list">
+ </span>
+
+ <div class="toolbar-end">
+ </div>
+ </div>
+
+ <div class='scrolled'>
+ <img src="{{ document.image.url }}" class='area-selectable' />
+ </div>
+</div>
--- /dev/null
+{% load i18n %}
+<li id="MotifsPerspective" data-ui-related="motifs-editor" data-ui-jsclass="MotifsPerspective">
+ <span>{% trans "Motifs" %}</span>
+</li>
--- /dev/null
+{% load i18n %}
+<div id="objects-editor" class="editor" style="display: none">
+ <div class="toolbar">
+ <input id='tag-name' title='{% trans "Object name" %}' />
+ <button id='add'>{% trans "Add" %}</button>
+
+ <span id="objects-list">
+ </span>
+
+ <div class="toolbar-end">
+ </div>
+ </div>
+
+ <div class='scrolled'>
+ <img src="{{ document.image.url }}" class='area-selectable' />
+ </div>
+</div>
--- /dev/null
+{% load i18n %}
+<li id="ObjectsPerspective" data-ui-related="objects-editor" data-ui-jsclass="ObjectsPerspective">
+ <span>{% trans "Objects" %}</span>
+</li>
--- /dev/null
+{% load toolbar_tags i18n %}
+<div id="source-editor" class="editor">
+ <textarea id="codemirror_placeholder"><br/></textarea>
+</div>
\ No newline at end of file
--- /dev/null
+{% load i18n %}
+<li id="CodeMirrorPerspective"
+ data-ui-related="source-editor"
+ data-ui-jsclass="CodeMirrorPerspective">
+ <span>{% trans "Source code" %}</span>
+</li>
\ No newline at end of file
--- /dev/null
+{% load i18n %}
+{% load wiki %}
+<div id="summary-view-editor" class="editor" style="display: none">
+ <!-- <div class="toolbar">
+ </div> -->
+ <div id="summary-view">
+ <h2>
+ <label for="title">{% trans "Title" %}:</label>
+ <span data-ui-editable="true" data-edit-target="meta.displayTitle"
+ >{{ document.name|wiki_title }}</span>
+ </h2>
+ <p>
+ <label>{% trans "Document ID" %}:</label>
+ <span>{{ document.name }}</span>
+ </p>
+ <p>
+ <label>{% trans "Current version" %}:</label>
+ {{ document_info.revision }} ({{document_info.date}})
+ <p>
+ <label>{% trans "Last edited by" %}:</label>
+ {{document_info.author}}
+ </p>
+ </div>
+</div>
--- /dev/null
+{% load i18n %}
+{% load wiki %}
+<li id="SummaryPerspective" data-ui-related="summary-view-editor" data-ui-jsclass="SummaryPerspective">
+ <span>{% trans "Summary" %}</span>
+</li>
--- /dev/null
+{% load i18n %}
+<div id="add_tag_dialog" class="dialog" data-ui-jsclass="AddTagDialog">
+ <form method="POST" action="#">
+ {% for field in forms.add_tag.visible_fields %}
+ <p>{{ field.label_tag }} {{ field }} <span data-ui-error-for="{{ field.name }}"> </span></p>
+ <p>{{ field.help_text }}</p>
+ {% endfor %}
+
+ {% for f in forms.add_tag.hidden_fields %}
+ {{ f }}
+ {% endfor %}
+ <p data-ui-error-for="__all__"> </p>
+
+ <p class="action_area">
+ <button type="submit" class"ok" data-ui-action="save">{% trans "Save" %}</button>
+ <button type="button" class="cancel" data-ui-action="cancel">{% trans "Cancel" %}</button>
+ </p>
+ </form>
+</div>
--- /dev/null
+from nose.tools import *
+import wiki.models as models
+import shutil
+import tempfile
+
+
+class TestStorageBase:
+ def setUp(self):
+ self.dirpath = tempfile.mkdtemp(prefix='nosetest_')
+
+ def tearDown(self):
+ shutil.rmtree(self.dirpath)
+
+
+class TestDocumentStorage(TestStorageBase):
+
+ def test_storage_empty(self):
+ storage = models.DocumentStorage(self.dirpath)
+ eq_(storage.all(), [])
--- /dev/null
+# -*- coding: utf-8
+from django.conf.urls.defaults import *
+
+
+urlpatterns = patterns('wiki_img.views',
+ url(r'^edit/(?P<slug>[^/]+)/$',
+ 'editor', name="wiki_img_editor"),
+
+ url(r'^readonly/(?P<slug>[^/]+)/$',
+ 'editor_readonly', name="wiki_img_editor_readonly"),
+
+ url(r'^text/(?P<image_id>\d+)/$',
+ 'text', name="wiki_img_text"),
+
+ url(r'^history/(?P<chunk_id>\d+)/$',
+ 'history', name="wiki_history"),
+
+)
--- /dev/null
+import os
+import functools
+import logging
+logger = logging.getLogger("fnp.wiki_img")
+
+from django.views.generic.simple import direct_to_template
+from django.core.urlresolvers import reverse
+from wiki.helpers import JSONResponse
+from django import http
+from django.shortcuts import get_object_or_404
+from django.views.decorators.http import require_GET
+from django.conf import settings
+from django.utils.formats import localize
+
+from catalogue.models import Image
+from wiki_img.forms import DocumentTextSaveForm
+
+#
+# Quick hack around caching problems, TODO: use ETags
+#
+from django.views.decorators.cache import never_cache
+
+
+@never_cache
+def editor(request, slug, template_name='wiki_img/document_details.html'):
+ doc = get_object_or_404(Image, slug=slug)
+
+ return direct_to_template(request, template_name, extra_context={
+ 'document': doc,
+ 'forms': {
+ "text_save": DocumentTextSaveForm(user=request.user, prefix="textsave"),
+ },
+ 'REDMINE_URL': settings.REDMINE_URL,
+ })
+
+
+@require_GET
+def editor_readonly(request, slug, template_name='wiki_img/document_details_readonly.html'):
+ doc = get_object_or_404(Image, slug=slug)
+ try:
+ revision = request.GET['revision']
+ except (KeyError):
+ raise Http404
+
+
+
+ return direct_to_template(request, template_name, extra_context={
+ 'document': doc,
+ 'revision': revision,
+ 'readonly': True,
+ 'REDMINE_URL': settings.REDMINE_URL,
+ })
+
+
+@never_cache
+def text(request, image_id):
+ doc = get_object_or_404(Image, pk=image_id)
+ if request.method == 'POST':
+ form = DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
+ if form.is_valid():
+ if request.user.is_authenticated():
+ author = request.user
+ else:
+ author = None
+ text = form.cleaned_data['text']
+ parent_revision = form.cleaned_data['parent_revision']
+ if parent_revision is not None:
+ parent = doc.at_revision(parent_revision)
+ else:
+ parent = None
+ stage = form.cleaned_data['stage_completed']
+ tags = [stage] if stage else []
+ publishable = (form.cleaned_data['publishable'] and
+ request.user.has_perm('catalogue.can_pubmark'))
+ doc.commit(author=author,
+ text=text,
+ parent=parent,
+ description=form.cleaned_data['comment'],
+ tags=tags,
+ author_name=form.cleaned_data['author_name'],
+ author_email=form.cleaned_data['author_email'],
+ publishable=publishable,
+ )
+ revision = doc.revision()
+ return JSONResponse({
+ 'text': doc.materialize() if parent_revision != revision else None,
+ 'meta': {},
+ 'revision': revision,
+ })
+ else:
+ return JSONFormInvalid(form)
+ else:
+ revision = request.GET.get("revision", None)
+
+ try:
+ revision = int(revision)
+ except (ValueError, TypeError):
+ revision = doc.revision()
+
+ if revision is not None:
+ text = doc.at_revision(revision).materialize()
+ else:
+ text = ''
+
+ return JSONResponse({
+ 'text': text,
+ 'meta': {},
+ 'revision': revision,
+ })
+
+
+@never_cache
+def history(request, chunk_id):
+ # TODO: pagination
+ doc = get_object_or_404(Image, pk=chunk_id)
+ if not doc.accessible(request):
+ return HttpResponseForbidden("Not authorized.")
+
+ changes = []
+ for change in doc.history().reverse():
+ changes.append({
+ "version": change.revision,
+ "description": change.description,
+ "author": change.author_str(),
+ "date": localize(change.created_at),
+ "publishable": _("Publishable") + "\n" if change.publishable else "",
+ "tag": ',\n'.join(unicode(tag) for tag in change.tags.all()),
+ })
+ return JSONResponse(changes)
'catalogue',
'dvcs',
'wiki',
+ 'wiki_img',
'toolbar',
'apiclient',
'email_mangler',
'css/summary.css',
'css/html.css',
'css/jquery.autocomplete.css',
+ 'css/imgareaselect-default.css', #img!
'css/dialogs.css',
),
'output_filename': 'compressed/detail_styles_?.css',
),
'output_filename': 'compressed/detail_scripts_?.js',
},
+ 'wiki_img': {
+ 'source_filenames': (
+ # libraries
+ 'js/lib/jquery-1.4.2.min.js',
+ 'js/lib/jquery/jquery.autocomplete.js',
+ 'js/lib/jquery/jquery.blockui.js',
+ 'js/lib/jquery/jquery.elastic.js',
+ 'js/lib/jquery/jquery.imgareaselect.js',
+ 'js/button_scripts.js',
+ 'js/slugify.js',
+
+ # wiki scripts
+ 'js/wiki_img/wikiapi.js',
+
+ # base UI
+ 'js/wiki_img/base.js',
+ 'js/wiki_img/toolbar.js',
+
+ # dialogs
+ 'js/wiki_img/dialog_save.js',
+ 'js/wiki_img/dialog_addtag.js',
+
+ # views
+ 'js/wiki_img/view_summary.js',
+ 'js/wiki_img/view_editor_objects.js',
+ 'js/wiki_img/view_editor_motifs.js',
+ 'js/wiki/view_editor_source.js',
+ 'js/wiki/view_history.js',
+ ),
+ 'output_filename': 'compressed/detail_img_scripts_?.js',
+ },
'catalogue': {
'source_filenames': (
'js/catalogue/catalogue.js',
--- /dev/null
+/*
+ * imgAreaSelect default style
+ */
+
+.imgareaselect-border1 {
+ background: url(/media/static/img/jquery.imgareaselect/border-v.gif) repeat-y left top;
+}
+
+.imgareaselect-border2 {
+ background: url(/media/static/img/jquery.imgareaselect/border-h.gif) repeat-x left top;
+}
+
+.imgareaselect-border3 {
+ background: url(/media/static/img/jquery.imgareaselect/border-v.gif) repeat-y right top;
+}
+
+.imgareaselect-border4 {
+ background: url(/media/static/img/jquery.imgareaselect/border-h.gif) repeat-x left bottom;
+}
+
+.imgareaselect-border1, .imgareaselect-border2,
+.imgareaselect-border3, .imgareaselect-border4 {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.imgareaselect-handle {
+ background-color: #fff;
+ border: solid 1px #000;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.imgareaselect-outer {
+ background-color: #000;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
+}
+
+.imgareaselect-selection {
+}
overflow: hidden;
}
+.sideless .editor {
+ right: 0;
+}
+.image-object {
+ padding-left: 1em;
+ font: 12px Sans, Helvetica, Verdana, sans-serif;
+}
+.image-object:hover {
+ cursor: pointer;
+}
+#objects-list .delete {
+ padding-left: 3px;
+ font: 10px Sans, Helvetica, Verdana, sans-serif;
+}
+#objects-list .delete:hover {
+ cursor: pointer;
+}
+
+#objects-list .active {
+ color: #800;
+}
+
+
#editor.readonly .editor {
right: 0px;
}
font: 11px Helvetica, Verdana, sans-serif;
font-weight: bold;
+
+ z-index: 100;
}
.saveNotify span {
font-weight: bold;
}
+
+
+
+.scrolled {
+ position: absolute;
+ top: 29px;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ overflow: auto;
+}
--- /dev/null
+/*
+ * imgAreaSelect jQuery plugin
+ * version 0.9.3
+ *
+ * Copyright (c) 2008-2010 Michal Wojciechowski (odyniec.net)
+ *
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://odyniec.net/projects/imgareaselect/
+ *
+ */
+
+(function($) {
+
+var abs = Math.abs,
+ max = Math.max,
+ min = Math.min,
+ round = Math.round;
+
+function div() {
+ return $('<div/>');
+}
+
+$.imgAreaSelect = function (img, options) {
+ var
+
+ $img = $(img),
+
+ imgLoaded,
+
+ $box = div(),
+ $area = div(),
+ $border = div().add(div()).add(div()).add(div()),
+ $outer = div().add(div()).add(div()).add(div()),
+ $handles = $([]),
+
+ $areaOpera,
+
+ left, top,
+
+ imgOfs,
+
+ imgWidth, imgHeight,
+
+ $parent,
+
+ parOfs,
+
+ zIndex = 0,
+
+ position = 'absolute',
+
+ startX, startY,
+
+ scaleX, scaleY,
+
+ resizeMargin = 10,
+
+ resize,
+
+ minWidth, minHeight, maxWidth, maxHeight,
+
+ aspectRatio,
+
+ shown,
+
+ x1, y1, x2, y2,
+
+ selection = { x1: 0, y1: 0, x2: 0, y2: 0, width: 0, height: 0 },
+
+ docElem = document.documentElement,
+
+ $p, d, i, o, w, h, adjusted;
+
+ function viewX(x) {
+ return x + imgOfs.left - parOfs.left;
+ }
+
+ function viewY(y) {
+ return y + imgOfs.top - parOfs.top;
+ }
+
+ function selX(x) {
+ return x - imgOfs.left + parOfs.left;
+ }
+
+ function selY(y) {
+ return y - imgOfs.top + parOfs.top;
+ }
+
+ function evX(event) {
+ return event.pageX - parOfs.left;
+ }
+
+ function evY(event) {
+ return event.pageY - parOfs.top;
+ }
+
+ function getSelection(noScale) {
+ var sx = noScale || scaleX, sy = noScale || scaleY;
+
+ return { x1: round(selection.x1 * sx),
+ y1: round(selection.y1 * sy),
+ x2: round(selection.x2 * sx),
+ y2: round(selection.y2 * sy),
+ width: round(selection.x2 * sx) - round(selection.x1 * sx),
+ height: round(selection.y2 * sy) - round(selection.y1 * sy) };
+ }
+
+ function setSelection(x1, y1, x2, y2, noScale) {
+ var sx = noScale || scaleX, sy = noScale || scaleY;
+
+ selection = {
+ x1: round(x1 / sx),
+ y1: round(y1 / sy),
+ x2: round(x2 / sx),
+ y2: round(y2 / sy)
+ };
+
+ selection.width = selection.x2 - selection.x1;
+ selection.height = selection.y2 - selection.y1;
+ }
+
+ function adjust() {
+ if (!$img.width())
+ return;
+
+ imgOfs = { left: round($img.offset().left), top: round($img.offset().top) };
+
+ imgWidth = $img.width();
+ imgHeight = $img.height();
+
+ minWidth = options.minWidth || 0;
+ minHeight = options.minHeight || 0;
+ maxWidth = min(options.maxWidth || 1<<24, imgWidth);
+ maxHeight = min(options.maxHeight || 1<<24, imgHeight);
+
+ if ($().jquery == '1.3.2' && position == 'fixed' &&
+ !docElem['getBoundingClientRect'])
+ {
+ imgOfs.top += max(document.body.scrollTop, docElem.scrollTop);
+ imgOfs.left += max(document.body.scrollLeft, docElem.scrollLeft);
+ }
+
+ parOfs = $.inArray($parent.css('position'), ['absolute', 'relative']) + 1 ?
+ { left: round($parent.offset().left) - $parent.scrollLeft(),
+ top: round($parent.offset().top) - $parent.scrollTop() } :
+ position == 'fixed' ?
+ { left: $(document).scrollLeft(), top: $(document).scrollTop() } :
+ { left: 0, top: 0 };
+
+ left = viewX(0);
+ top = viewY(0);
+
+ if (selection.x2 > imgWidth || selection.y2 > imgHeight)
+ doResize();
+ }
+
+ function update(resetKeyPress) {
+ if (!shown) return;
+
+ $box.css({ left: viewX(selection.x1), top: viewY(selection.y1) })
+ .add($area).width(w = selection.width).height(h = selection.height);
+
+ $area.add($border).add($handles).css({ left: 0, top: 0 });
+
+ $border
+ .width(max(w - $border.outerWidth() + $border.innerWidth(), 0))
+ .height(max(h - $border.outerHeight() + $border.innerHeight(), 0));
+
+ $($outer[0]).css({ left: left, top: top,
+ width: selection.x1, height: imgHeight });
+ $($outer[1]).css({ left: left + selection.x1, top: top,
+ width: w, height: selection.y1 });
+ $($outer[2]).css({ left: left + selection.x2, top: top,
+ width: imgWidth - selection.x2, height: imgHeight });
+ $($outer[3]).css({ left: left + selection.x1, top: top + selection.y2,
+ width: w, height: imgHeight - selection.y2 });
+
+ w -= $handles.outerWidth();
+ h -= $handles.outerHeight();
+
+ switch ($handles.length) {
+ case 8:
+ $($handles[4]).css({ left: w / 2 });
+ $($handles[5]).css({ left: w, top: h / 2 });
+ $($handles[6]).css({ left: w / 2, top: h });
+ $($handles[7]).css({ top: h / 2 });
+ case 4:
+ $handles.slice(1,3).css({ left: w });
+ $handles.slice(2,4).css({ top: h });
+ }
+
+ if (resetKeyPress !== false) {
+ if ($.imgAreaSelect.keyPress != docKeyPress)
+ $(document).unbind($.imgAreaSelect.keyPress,
+ $.imgAreaSelect.onKeyPress);
+
+ if (options.keys)
+ $(document)[$.imgAreaSelect.keyPress](
+ $.imgAreaSelect.onKeyPress = docKeyPress);
+ }
+
+ if ($.browser.msie && $border.outerWidth() - $border.innerWidth() == 2) {
+ $border.css('margin', 0);
+ setTimeout(function () { $border.css('margin', 'auto'); }, 0);
+ }
+ }
+
+ function doUpdate(resetKeyPress) {
+ adjust();
+ update(resetKeyPress);
+ x1 = viewX(selection.x1); y1 = viewY(selection.y1);
+ x2 = viewX(selection.x2); y2 = viewY(selection.y2);
+ }
+
+ function hide($elem, fn) {
+ options.fadeSpeed ? $elem.fadeOut(options.fadeSpeed, fn) : $elem.hide();
+
+ }
+
+ function areaMouseMove(event) {
+ var x = selX(evX(event)) - selection.x1,
+ y = selY(evY(event)) - selection.y1;
+
+ if (!adjusted) {
+ adjust();
+ adjusted = true;
+
+ $box.one('mouseout', function () { adjusted = false; });
+ }
+
+ resize = '';
+
+ if (options.resizable) {
+ if (y <= resizeMargin)
+ resize = 'n';
+ else if (y >= selection.height - resizeMargin)
+ resize = 's';
+ if (x <= resizeMargin)
+ resize += 'w';
+ else if (x >= selection.width - resizeMargin)
+ resize += 'e';
+ }
+
+ $box.css('cursor', resize ? resize + '-resize' :
+ options.movable ? 'move' : '');
+ if ($areaOpera)
+ $areaOpera.toggle();
+ }
+
+ function docMouseUp(event) {
+ $('body').css('cursor', '');
+
+ if (options.autoHide || selection.width * selection.height == 0)
+ hide($box.add($outer), function () { $(this).hide(); });
+
+ options.onSelectEnd(img, getSelection());
+
+ $(document).unbind('mousemove', selectingMouseMove);
+ $box.mousemove(areaMouseMove);
+ }
+
+ function areaMouseDown(event) {
+ if (event.which != 1) return false;
+
+ adjust();
+
+ if (resize) {
+ $('body').css('cursor', resize + '-resize');
+
+ x1 = viewX(selection[/w/.test(resize) ? 'x2' : 'x1']);
+ y1 = viewY(selection[/n/.test(resize) ? 'y2' : 'y1']);
+
+ $(document).mousemove(selectingMouseMove)
+ .one('mouseup', docMouseUp);
+ $box.unbind('mousemove', areaMouseMove);
+ }
+ else if (options.movable) {
+ startX = left + selection.x1 - evX(event);
+ startY = top + selection.y1 - evY(event);
+
+ $box.unbind('mousemove', areaMouseMove);
+
+ $(document).mousemove(movingMouseMove)
+ .one('mouseup', function () {
+ options.onSelectEnd(img, getSelection());
+
+ $(document).unbind('mousemove', movingMouseMove);
+ $box.mousemove(areaMouseMove);
+ });
+ }
+ else
+ $img.mousedown(event);
+
+ return false;
+ }
+
+ function fixAspectRatio(xFirst) {
+ if (aspectRatio)
+ if (xFirst) {
+ x2 = max(left, min(left + imgWidth,
+ x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1)));
+
+ y2 = round(max(top, min(top + imgHeight,
+ y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1))));
+ x2 = round(x2);
+ }
+ else {
+ y2 = max(top, min(top + imgHeight,
+ y1 + abs(x2 - x1) / aspectRatio * (y2 > y1 || -1)));
+ x2 = round(max(left, min(left + imgWidth,
+ x1 + abs(y2 - y1) * aspectRatio * (x2 > x1 || -1))));
+ y2 = round(y2);
+ }
+ }
+
+ function doResize() {
+ x1 = min(x1, left + imgWidth);
+ y1 = min(y1, top + imgHeight);
+
+ if (abs(x2 - x1) < minWidth) {
+ x2 = x1 - minWidth * (x2 < x1 || -1);
+
+ if (x2 < left)
+ x1 = left + minWidth;
+ else if (x2 > left + imgWidth)
+ x1 = left + imgWidth - minWidth;
+ }
+
+ if (abs(y2 - y1) < minHeight) {
+ y2 = y1 - minHeight * (y2 < y1 || -1);
+
+ if (y2 < top)
+ y1 = top + minHeight;
+ else if (y2 > top + imgHeight)
+ y1 = top + imgHeight - minHeight;
+ }
+
+ x2 = max(left, min(x2, left + imgWidth));
+ y2 = max(top, min(y2, top + imgHeight));
+
+ fixAspectRatio(abs(x2 - x1) < abs(y2 - y1) * aspectRatio);
+
+ if (abs(x2 - x1) > maxWidth) {
+ x2 = x1 - maxWidth * (x2 < x1 || -1);
+ fixAspectRatio();
+ }
+
+ if (abs(y2 - y1) > maxHeight) {
+ y2 = y1 - maxHeight * (y2 < y1 || -1);
+ fixAspectRatio(true);
+ }
+
+ selection = { x1: selX(min(x1, x2)), x2: selX(max(x1, x2)),
+ y1: selY(min(y1, y2)), y2: selY(max(y1, y2)),
+ width: abs(x2 - x1), height: abs(y2 - y1) };
+
+ update();
+
+ options.onSelectChange(img, getSelection());
+ }
+
+ function selectingMouseMove(event) {
+ x2 = resize == '' || /w|e/.test(resize) || aspectRatio ? evX(event) : viewX(selection.x2);
+ y2 = resize == '' || /n|s/.test(resize) || aspectRatio ? evY(event) : viewY(selection.y2);
+
+ doResize();
+
+ return false;
+
+ }
+
+ function doMove(newX1, newY1) {
+ x2 = (x1 = newX1) + selection.width;
+ y2 = (y1 = newY1) + selection.height;
+
+ $.extend(selection, { x1: selX(x1), y1: selY(y1), x2: selX(x2),
+ y2: selY(y2) });
+
+ update();
+
+ options.onSelectChange(img, getSelection());
+ }
+
+ function movingMouseMove(event) {
+ x1 = max(left, min(startX + evX(event), left + imgWidth - selection.width));
+ y1 = max(top, min(startY + evY(event), top + imgHeight - selection.height));
+
+ doMove(x1, y1);
+
+ event.preventDefault();
+
+ return false;
+ }
+
+ function startSelection() {
+ adjust();
+
+ x2 = x1;
+ y2 = y1;
+
+ doResize();
+
+ resize = '';
+
+ if ($outer.is(':not(:visible)'))
+ $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
+
+ shown = true;
+
+ $(document).unbind('mouseup', cancelSelection)
+ .mousemove(selectingMouseMove).one('mouseup', docMouseUp);
+ $box.unbind('mousemove', areaMouseMove);
+
+ options.onSelectStart(img, getSelection());
+ }
+
+ function cancelSelection() {
+ $(document).unbind('mousemove', startSelection);
+ hide($box.add($outer));
+
+ selection = { x1: selX(x1), y1: selY(y1), x2: selX(x1), y2: selY(y1),
+ width: 0, height: 0 };
+
+ options.onSelectChange(img, getSelection());
+ options.onSelectEnd(img, getSelection());
+ }
+
+ function imgMouseDown(event) {
+ if (event.which != 1 || $outer.is(':animated')) return false;
+
+ adjust();
+ startX = x1 = evX(event);
+ startY = y1 = evY(event);
+
+ $(document).one('mousemove', startSelection)
+ .one('mouseup', cancelSelection);
+
+ return false;
+ }
+
+ function windowResize() {
+ doUpdate(false);
+ }
+
+ function imgLoad() {
+ imgLoaded = true;
+
+ setOptions(options = $.extend({
+ classPrefix: 'imgareaselect',
+ movable: true,
+ resizable: true,
+ parent: 'body',
+ onInit: function () {},
+ onSelectStart: function () {},
+ onSelectChange: function () {},
+ onSelectEnd: function () {}
+ }, options));
+
+ $box.add($outer).css({ visibility: '' });
+
+ if (options.show) {
+ shown = true;
+ adjust();
+ update();
+ $box.add($outer).hide().fadeIn(options.fadeSpeed||0);
+ }
+
+ setTimeout(function () { options.onInit(img, getSelection()); }, 0);
+ }
+
+ var docKeyPress = function(event) {
+ var k = options.keys, d, t, key = event.keyCode;
+
+ d = !isNaN(k.alt) && (event.altKey || event.originalEvent.altKey) ? k.alt :
+ !isNaN(k.ctrl) && event.ctrlKey ? k.ctrl :
+ !isNaN(k.shift) && event.shiftKey ? k.shift :
+ !isNaN(k.arrows) ? k.arrows : 10;
+
+ if (k.arrows == 'resize' || (k.shift == 'resize' && event.shiftKey) ||
+ (k.ctrl == 'resize' && event.ctrlKey) ||
+ (k.alt == 'resize' && (event.altKey || event.originalEvent.altKey)))
+ {
+ switch (key) {
+ case 37:
+ d = -d;
+ case 39:
+ t = max(x1, x2);
+ x1 = min(x1, x2);
+ x2 = max(t + d, x1);
+ fixAspectRatio();
+ break;
+ case 38:
+ d = -d;
+ case 40:
+ t = max(y1, y2);
+ y1 = min(y1, y2);
+ y2 = max(t + d, y1);
+ fixAspectRatio(true);
+ break;
+ default:
+ return;
+ }
+
+ doResize();
+ }
+ else {
+ x1 = min(x1, x2);
+ y1 = min(y1, y2);
+
+ switch (key) {
+ case 37:
+ doMove(max(x1 - d, left), y1);
+ break;
+ case 38:
+ doMove(x1, max(y1 - d, top));
+ break;
+ case 39:
+ doMove(x1 + min(d, imgWidth - selX(x2)), y1);
+ break;
+ case 40:
+ doMove(x1, y1 + min(d, imgHeight - selY(y2)));
+ break;
+ default:
+ return;
+ }
+ }
+
+ return false;
+ };
+
+ function styleOptions($elem, props) {
+ for (option in props)
+ if (options[option] !== undefined)
+ $elem.css(props[option], options[option]);
+ }
+
+ function setOptions(newOptions) {
+ if (newOptions.parent)
+ ($parent = $(newOptions.parent)).append($box.add($outer));
+
+ $.extend(options, newOptions);
+
+ adjust();
+
+ if (newOptions.handles != null) {
+ $handles.remove();
+ $handles = $([]);
+
+ i = newOptions.handles ? newOptions.handles == 'corners' ? 4 : 8 : 0;
+
+ while (i--)
+ $handles = $handles.add(div());
+
+ $handles.addClass(options.classPrefix + '-handle').css({
+ position: 'absolute',
+ fontSize: 0,
+ zIndex: zIndex + 1 || 1
+ });
+
+ if (!parseInt($handles.css('width')) >= 0)
+ $handles.width(5).height(5);
+
+ if (o = options.borderWidth)
+ $handles.css({ borderWidth: o, borderStyle: 'solid' });
+
+ styleOptions($handles, { borderColor1: 'border-color',
+ borderColor2: 'background-color',
+ borderOpacity: 'opacity' });
+ }
+
+ scaleX = options.imageWidth / imgWidth || 1;
+ scaleY = options.imageHeight / imgHeight || 1;
+
+ if (newOptions.x1 != null) {
+ setSelection(newOptions.x1, newOptions.y1, newOptions.x2,
+ newOptions.y2);
+ newOptions.show = !newOptions.hide;
+ }
+
+ if (newOptions.keys)
+ options.keys = $.extend({ shift: 1, ctrl: 'resize' },
+ newOptions.keys);
+
+ $outer.addClass(options.classPrefix + '-outer');
+ $area.addClass(options.classPrefix + '-selection');
+ for (i = 0; i++ < 4;)
+ $($border[i-1]).addClass(options.classPrefix + '-border' + i);
+
+ styleOptions($area, { selectionColor: 'background-color',
+ selectionOpacity: 'opacity' });
+ styleOptions($border, { borderOpacity: 'opacity',
+ borderWidth: 'border-width' });
+ styleOptions($outer, { outerColor: 'background-color',
+ outerOpacity: 'opacity' });
+ if (o = options.borderColor1)
+ $($border[0]).css({ borderStyle: 'solid', borderColor: o });
+ if (o = options.borderColor2)
+ $($border[1]).css({ borderStyle: 'dashed', borderColor: o });
+
+ $box.append($area.add($border).add($handles).add($areaOpera));
+
+ if ($.browser.msie) {
+ if (o = $outer.css('filter').match(/opacity=([0-9]+)/))
+ $outer.css('opacity', o[1]/100);
+ if (o = $border.css('filter').match(/opacity=([0-9]+)/))
+ $border.css('opacity', o[1]/100);
+ }
+
+ if (newOptions.hide)
+ hide($box.add($outer));
+ else if (newOptions.show && imgLoaded) {
+ shown = true;
+ $box.add($outer).fadeIn(options.fadeSpeed||0);
+ doUpdate();
+ }
+
+ aspectRatio = (d = (options.aspectRatio || '').split(/:/))[0] / d[1];
+
+ $img.add($outer).unbind('mousedown', imgMouseDown);
+
+ if (options.disable || options.enable === false) {
+ $box.unbind('mousemove', areaMouseMove).unbind('mousedown', areaMouseDown);
+ $(window).unbind('resize', windowResize);
+ }
+ else {
+ if (options.enable || options.disable === false) {
+ if (options.resizable || options.movable)
+ $box.mousemove(areaMouseMove).mousedown(areaMouseDown);
+
+ $(window).resize(windowResize);
+ }
+
+ if (!options.persistent)
+ $img.add($outer).mousedown(imgMouseDown);
+ }
+
+ options.enable = options.disable = undefined;
+ }
+
+ this.remove = function () {
+ $img.unbind('mousedown', imgMouseDown);
+ $box.add($outer).remove();
+ };
+
+ this.getOptions = function () { return options; };
+
+ this.setOptions = setOptions;
+
+ this.getSelection = getSelection;
+
+ this.setSelection = setSelection;
+
+ this.update = doUpdate;
+
+ $p = $img;
+
+ while ($p.length) {
+ zIndex = max(zIndex,
+ !isNaN($p.css('z-index')) ? $p.css('z-index') : zIndex);
+ if ($p.css('position') == 'fixed')
+ position = 'fixed';
+
+ $p = $p.parent(':not(body)');
+ }
+
+ zIndex = options.zIndex || zIndex;
+
+ if ($.browser.msie)
+ $img.attr('unselectable', 'on');
+
+ $.imgAreaSelect.keyPress = $.browser.msie ||
+ $.browser.safari ? 'keydown' : 'keypress';
+
+ if ($.browser.opera)
+ $areaOpera = div().css({ width: '100%', height: '100%',
+ position: 'absolute', zIndex: zIndex + 2 || 2 });
+
+ $box.add($outer).css({ visibility: 'hidden', position: position,
+ overflow: 'hidden', zIndex: zIndex || '0' });
+ $box.css({ zIndex: zIndex + 2 || 2 });
+ $area.add($border).css({ position: 'absolute', fontSize: 0 });
+
+ img.complete || img.readyState == 'complete' || !$img.is('img') ?
+ imgLoad() : $img.one('load', imgLoad);
+};
+
+$.fn.imgAreaSelect = function (options) {
+ options = options || {};
+
+ this.each(function () {
+ if ($(this).data('imgAreaSelect')) {
+ if (options.remove) {
+ $(this).data('imgAreaSelect').remove();
+ $(this).removeData('imgAreaSelect');
+ }
+ else
+ $(this).data('imgAreaSelect').setOptions(options);
+ }
+ else if (!options.remove) {
+ if (options.enable === undefined && options.disable === undefined)
+ options.enable = true;
+
+ $(this).data('imgAreaSelect', new $.imgAreaSelect(this, options));
+ }
+ });
+
+ if (options.instance)
+ return $(this).data('imgAreaSelect');
+
+ return this;
+};
+
+})(jQuery);
--- /dev/null
+(function($)
+{
+ var noop = function() { };
+
+ $.wiki = {
+ perspectives: {},
+ cls: {},
+ state: {
+ "version": 1,
+ "perspectives": {
+ "CodeMirrorPerspective": {}
+ }
+ }
+ };
+
+ $.wiki.loadConfig = function() {
+ if(!window.localStorage)
+ return;
+
+ try {
+ var value = window.localStorage.getItem(CurrentDocument.id) || "{}";
+ var config = JSON.parse(value);
+
+ if (config.version == $.wiki.state.version) {
+ $.wiki.state.perspectives = $.extend($.wiki.state.perspectives, config.perspectives);
+ }
+ } catch(e) {
+ console.log("Failed to load config, using default.");
+ }
+
+ console.log("Loaded:", $.wiki.state, $.wiki.state.version);
+ };
+
+ $(window).bind('unload', function() {
+ if(window.localStorage)
+ window.localStorage.setItem(CurrentDocument.id, JSON.stringify($.wiki.state));
+ })
+
+
+ $.wiki.activePerspective = function() {
+ return this.perspectives[$("#tabs li.active").attr('id')];
+ };
+
+ $.wiki.exitContext = function() {
+ var ap = this.activePerspective();
+ if(ap) ap.onExit();
+ return ap;
+ };
+
+ $.wiki.enterContext = function(ap) {
+ if(ap) ap.onEnter();
+ };
+
+ $.wiki.isDirty = function() {
+ var ap = this.activePerspective();
+ return (!!CurrentDocument && CurrentDocument.has_local_changes) || ap.dirty();
+ };
+
+ $.wiki.newTab = function(doc, title, klass) {
+ var base_id = 'id' + Math.floor(Math.random()* 5000000000);
+ var id = (''+klass)+'_' + base_id;
+ var $tab = $('<li id="'+id+'" data-ui-related="'+base_id+'" data-ui-jsclass="'+klass+'" >'
+ + title + '<img src="'+STATIC_URL+'icons/close.png" class="tabclose"></li>');
+ var $view = $('<div class="editor '+klass+'" id="'+base_id+'"> </div>');
+
+ this.perspectives[id] = new $.wiki[klass]({
+ doc: doc,
+ id: id,
+ base_id: base_id,
+ });
+
+ $('#tabs').append($tab);
+ $view.hide().appendTo('#editor');
+ return {
+ tab: $tab[0],
+ view: $view[0],
+ };
+ };
+
+ $.wiki.initTab = function(options) {
+ var klass = $(options.tab).attr('data-ui-jsclass');
+
+ return new $.wiki[klass]({
+ doc: options.doc,
+ id: $(options.tab).attr('id'),
+ callback: function() {
+ $.wiki.perspectives[this.perspective_id] = this;
+ if(options.callback)
+ options.callback.call(this);
+ }
+ });
+ };
+
+ $.wiki.perspectiveForTab = function(tab) { // element or id
+ return this.perspectives[ $(tab).attr('id')];
+ }
+
+ $.wiki.switchToTab = function(tab){
+ var self = this;
+ var $tab = $(tab);
+
+ if($tab.length != 1)
+ $tab = $(DEFAULT_PERSPECTIVE);
+
+ var $old = $tab.closest('.tabs').find('.active');
+
+ $old.each(function(){
+ $(this).removeClass('active');
+ self.perspectives[$(this).attr('id')].onExit();
+ $('#' + $(this).attr('data-ui-related')).hide();
+ });
+
+ /* show new */
+ $tab.addClass('active');
+ $('#' + $tab.attr('data-ui-related')).show();
+
+ console.log($tab);
+ console.log($.wiki.perspectives);
+
+ $.wiki.perspectives[$tab.attr('id')].onEnter();
+ };
+
+ /*
+ * Basic perspective.
+ */
+ $.wiki.Perspective = function(options) {
+ if(!options) return;
+
+ this.doc = options.doc;
+ if (options.id) {
+ this.perspective_id = options.id;
+ }
+ else {
+ this.perspective_id = '';
+ }
+
+ if(options.callback)
+ options.callback.call(this);
+ };
+
+ $.wiki.Perspective.prototype.config = function() {
+ return $.wiki.state.perspectives[this.perspective_id];
+ }
+
+ $.wiki.Perspective.prototype.toString = function() {
+ return this.perspective_id;
+ };
+
+ $.wiki.Perspective.prototype.dirty = function() {
+ return true;
+ };
+
+ $.wiki.Perspective.prototype.onEnter = function () {
+ // called when perspective in initialized
+ if (!this.noupdate_hash_onenter) {
+ document.location.hash = '#' + this.perspective_id;
+ }
+ };
+
+ $.wiki.Perspective.prototype.onExit = function () {
+ // called when user switches to another perspective
+ if (!this.noupdate_hash_onenter) {
+ document.location.hash = '';
+ }
+ };
+
+ $.wiki.Perspective.prototype.destroy = function() {
+ // pass
+ };
+
+ $.wiki.Perspective.prototype.freezeState = function () {
+ // free UI state (don't store data here)
+ };
+
+ $.wiki.Perspective.prototype.unfreezeState = function (frozenState) {
+ // restore UI state
+ };
+
+ /*
+ * Stub rendering (used in generating history)
+ */
+ $.wiki.renderStub = function(params)
+ {
+ params = $.extend({ 'filters': {} }, params);
+ var $elem = params.stub.clone();
+ $elem.removeClass('row-stub');
+ params.container.append($elem);
+
+ $('*[data-stub-value]', $elem).each(function() {
+ var $this = $(this);
+ var field = $this.attr('data-stub-value');
+
+ var value = params.data[field];
+
+ if(params.filters[field])
+ value = params.filters[field](value);
+
+ if(value === null || value === undefined) return;
+
+ if(!$this.attr('data-stub-target')) {
+ $this.text(value);
+ }
+ else {
+ $this.attr($this.attr('data-stub-target'), value);
+ $this.removeAttr('data-stub-target');
+ $this.removeAttr('data-stub-value');
+ }
+ });
+
+ $elem.show();
+ return $elem;
+ };
+
+ /*
+ * Dialogs
+ */
+ function GenericDialog(element) {
+ if(!element) return;
+
+ var self = this;
+
+ self.$elem = $(element);
+
+ if(!self.$elem.attr('data-ui-initialized')) {
+ console.log("Initializing dialog", this);
+ self.initialize();
+ self.$elem.attr('data-ui-initialized', true);
+ }
+
+ self.show();
+ };
+
+ GenericDialog.prototype = {
+
+ /*
+ * Steps to follow when the dialog in first loaded on page.
+ */
+ initialize: function(){
+ var self = this;
+
+ /* bind buttons */
+ $('button[data-ui-action]', self.$elem).click(function(event) {
+ event.preventDefault();
+
+ var action = $(this).attr('data-ui-action');
+ console.log("Button pressed, action: ", action);
+
+ try {
+ self[action + "Action"].call(self);
+ } catch(e) {
+ console.log("Action failed:", e);
+ // always hide on cancel
+ if(action == 'cancel')
+ self.hide();
+ }
+ });
+ },
+
+ /*
+ * Prepare dialog for user. Clear any unnessary data.
+ */
+ show: function() {
+ $.blockUI({
+ message: this.$elem,
+ css: {
+ 'top': '25%',
+ 'left': '25%',
+ 'width': '50%'
+ }
+ });
+ },
+
+ hide: function(){
+ $.unblockUI();
+ },
+
+ cancelAction: function() {
+ this.hide();
+ },
+
+ doneAction: function() {
+ this.hide();
+ },
+
+ clearForm: function() {
+ $("*[data-ui-error-for]", this.$elem).text('');
+ },
+
+ reportErrors: function(errors) {
+ var global = $("*[data-ui-error-for='__all__']", this.$elem);
+ var unassigned = [];
+
+ for (var field_name in errors)
+ {
+ var span = $("*[data-ui-error-for='"+field_name+"']", this.$elem);
+
+ if(!span.length) {
+ unassigned.push(field_name);
+ continue;
+ }
+
+ span.text(errors[field_name].join(' '));
+ }
+
+ if(unassigned.length > 0)
+ global.text( global.text() + 'W formularzu wystąpiły błędy');
+ }
+ };
+
+ $.wiki.cls.GenericDialog = GenericDialog;
+
+ $.wiki.showDialog = function(selector, options) {
+ var elem = $(selector);
+
+ if(elem.length != 1) {
+ console.log("Failed to show dialog:", selector, elem);
+ return false;
+ }
+
+ try {
+ var klass = elem.attr('data-ui-jsclass');
+ return new $.wiki.cls[klass](elem, options);
+ } catch(e) {
+ console.log("Failed to show dialog", selector, klass, e);
+ return false;
+ }
+ };
+
+})(jQuery);
--- /dev/null
+/*
+ * Dialog for saving document to the server
+ *
+ */
+(function($){
+
+ function AddTagDialog(element, options){
+ if (!options.revision && options.revision != 0)
+ throw "AddTagDialog needs a revision number.";
+
+ this.ctx = $.wiki.exitContext();
+ this.clearForm();
+
+ /* fill out hidden fields */
+ this.$form = $('form', element);
+
+ $("input[name='addtag-id']", this.$form).val(CurrentDocument.id);
+ $("input[name='addtag-revision']", this.$form).val(options.revision);
+
+ $.wiki.cls.GenericDialog.call(this, element);
+ };
+
+ AddTagDialog.prototype = $.extend(new $.wiki.cls.GenericDialog(), {
+ cancelAction: function(){
+ $.wiki.enterContext(this.ctx);
+ this.hide();
+ },
+
+ saveAction: function(){
+ var self = this;
+
+ self.$elem.block({
+ message: "Dodawanie tagu",
+ fadeIn: 0,
+ });
+
+ CurrentDocument.setTag({
+ form: self.$form,
+ success: function(doc, changed, info){
+ self.$elem.block({
+ message: info,
+ timeout: 2000,
+ fadeOut: 0,
+ onUnblock: function(){
+ self.hide();
+ $.wiki.enterContext(self.ctx);
+ }
+ });
+ },
+ failure: function(doc, info){
+ console.log("Failure", info);
+ self.reportErrors(info);
+ self.$elem.unblock();
+ }
+ });
+ }
+ });
+
+ /* make it global */
+ $.wiki.cls.AddTagDialog = AddTagDialog;
+})(jQuery);
--- /dev/null
+/*
+ * Dialog for saving document to the server
+ *
+ */
+(function($) {
+
+ function SaveDialog(element) {
+ this.ctx = $.wiki.exitContext();
+ this.clearForm();
+
+ /* fill out hidden fields */
+ this.$form = $('form', element);
+
+ $("input[name='textsave-id']", this.$form).val(CurrentDocument.id);
+ $("input[name='textsave-parent_commit']", this.$form).val(CurrentDocument.commit);
+
+ $.wiki.cls.GenericDialog.call(this, element);
+ };
+
+ SaveDialog.prototype = new $.wiki.cls.GenericDialog();
+
+ SaveDialog.prototype.cancelAction = function() {
+ $.wiki.enterContext(this.ctx);
+ this.hide();
+ };
+
+ SaveDialog.prototype.saveAction = function() {
+ var self = this;
+
+ self.$elem.block({
+ message: "Zapisywanie... <br/><button id='save-hide'>ukryj</button>",
+ fadeIn: 0,
+ });
+ $.wiki.blocking = self.$elem;
+
+ try {
+
+ CurrentDocument.save({
+ form: self.$form,
+ success: function(doc, changed, info){
+ self.$elem.block({
+ message: info,
+ timeout: 2000,
+ fadeOut: 0,
+ onUnblock: function() {
+ self.hide();
+ $.wiki.enterContext(self.ctx);
+ }
+ });
+ },
+ failure: function(doc, info) {
+ console.log("Failure", info);
+ self.reportErrors(info);
+ self.$elem.unblock();
+ }
+ });
+ } catch(e) {
+ console.log('Exception:', e)
+ self.$elem.unblock();
+ }
+ }; /* end of save dialog */
+
+ /* make it global */
+ $.wiki.cls.SaveDialog = SaveDialog;
+})(jQuery);
--- /dev/null
+if (!window.console) {
+ window.console = {
+ log: function(){
+ }
+ }
+}
+
+DEFAULT_PERSPECTIVE = "#MotifsPerspective";
+
+$(function()
+{
+ var tabs = $('ol#tabs li');
+ var gallery = null;
+ CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+
+ $.blockUI.defaults.baseZ = 10000;
+
+ function initialize()
+ {
+ $(document).keydown(function(event) {
+ console.log("Received key:", event);
+ });
+
+ /* The save button */
+ $('#save-button').click(function(event){
+ event.preventDefault();
+ $.wiki.showDialog('#save_dialog');
+ });
+
+ $('.editor').hide();
+
+ /*
+ * TABS
+ */
+ $('.tabs li').live('click', function(event, callback) {
+ $.wiki.switchToTab(this);
+ });
+
+ $('#tabs li > .tabclose').live('click', function(event, callback) {
+ var $tab = $(this).parent();
+
+ if($tab.is('.active'))
+ $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+ var p = $.wiki.perspectiveForTab($tab);
+ p.destroy();
+
+ return false;
+ });
+
+
+ /*$(window).resize(function(){
+ $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+ });
+
+ $(window).resize();*/
+
+ /*$('.vsplitbar').toggle(
+ function() {
+ $.wiki.state.perspectives.ScanGalleryPerspective.show = true;
+ $('#sidebar').show();
+ $('.vsplitbar').css('right', 480).addClass('active');
+ $('#editor .editor').css('right', 510);
+ $(window).resize();
+ $.wiki.perspectiveForTab('#tabs-right .active').onEnter();
+ },
+ function() {
+ var active_right = $.wiki.perspectiveForTab('#tabs-right .active');
+ $.wiki.state.perspectives.ScanGalleryPerspective.show = false;
+ $('#sidebar').hide();
+ $('.vsplitbar').css('right', 0).removeClass('active');
+ $(".vsplitbar-title").html("↑ " + active_right.vsplitbar + " ↑");
+ $('#editor .editor').css('right', 30);
+ $(window).resize();
+ active_right.onExit();
+ }
+ );*/
+
+ window.onbeforeunload = function(e) {
+ if($.wiki.isDirty()) {
+ e.returnValue = "Na stronie mogą być nie zapisane zmiany.";
+ return "Na stronie mogą być nie zapisane zmiany.";
+ };
+ };
+
+ console.log("Fetching document's text");
+
+ $(document).bind('wlapi_document_changed', function(event, doc) {
+ try {
+ $('#document-revision').text(doc.revision);
+ } catch(e) {
+ console.log("Failed handler", e);
+ }
+ });
+
+ CurrentDocument.fetch({
+ success: function(){
+ console.log("Fetch success");
+ $('#loading-overlay').fadeOut();
+ var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+ console.log("Initial tab is:", active_tab)
+ $.wiki.switchToTab(active_tab);
+ },
+ failure: function() {
+ $('#loading-overlay').fadeOut();
+ alert("FAILURE");
+ }
+ });
+ }; /* end of initialize() */
+
+
+ /* Load configuration */
+ $.wiki.loadConfig();
+
+ var initAll = function(a, f) {
+ if (a.length == 0) return f();
+
+ $.wiki.initTab({
+ tab: a.pop(),
+ doc: CurrentDocument,
+ callback: function(){
+ initAll(a, f);
+ }
+ });
+ };
+
+
+ /*
+ * Initialize all perspectives
+ */
+ initAll( $.makeArray($('.tabs li')), initialize);
+ console.log(location.hash);
+});
+
+
--- /dev/null
+if (!window.console) {
+ window.console = {
+ log: function(){
+ }
+ }
+}
+
+
+DEFAULT_PERSPECTIVE = "#MotifsPerspective";
+
+$(function()
+{
+ var tabs = $('ol#tabs li');
+ var gallery = null;
+
+ CurrentDocument = new $.wikiapi.WikiDocument("document-meta");
+ $.blockUI.defaults.baseZ = 10000;
+
+ function initialize()
+ {
+ $('.editor').hide();
+
+ /*
+ * TABS
+ */
+ $('#tabs li').live('click', function(event, callback) {
+ $.wiki.switchToTab(this);
+ });
+
+ $('#tabs li > .tabclose').live('click', function(event, callback) {
+ var $tab = $(this).parent();
+
+ if($tab.is('.active'))
+ $.wiki.switchToTab(DEFAULT_PERSPECTIVE);
+
+ var p = $.wiki.perspectiveForTab($tab);
+ p.destroy();
+ return false;
+ });
+
+ $(window).resize(function(){
+ $('iframe').height($(window).height() - $('#tabs').outerHeight() - $('#source-editor .toolbar').outerHeight());
+ });
+
+ $(document).bind('wlapi_document_changed', function(event, doc) {
+ try {
+ $('#document-revision').text(doc.revision);
+ } catch(e) {
+ console.log("Failed handler", e);
+ }
+ });
+
+ CurrentDocument.fetch({
+ success: function(){
+ console.log("Fetch success");
+ $('#loading-overlay').fadeOut();
+ var active_tab = document.location.hash || DEFAULT_PERSPECTIVE;
+
+ $(window).resize();
+
+ console.log("Initial tab is:", active_tab)
+ $.wiki.switchToTab(active_tab);
+ },
+ failure: function() {
+ $('#loading-overlay').fadeOut();
+ alert("FAILURE");
+ }
+ });
+ }; /* end of initialize() */
+
+ /* Load configuration */
+ $.wiki.loadConfig();
+
+ var initAll = function(a, f) {
+ if (a.length == 0) return f();
+
+ $.wiki.initTab({
+ tab: a.pop(),
+ doc: CurrentDocument,
+ callback: function(){
+ initAll(a, f);
+ }
+ });
+ };
+
+
+ /*
+ * Initialize all perspectives
+ */
+ initAll( $.makeArray($('ol#tabs li')), initialize);
+ console.log(location.hash);
+});
--- /dev/null
+/*
+ * Toolbar plugin.
+ */
+(function($) {
+
+ $.fn.toolbarize = function(options) {
+ var $toolbar = $(this);
+ var $container = $('.button_group_container', $toolbar);
+
+ $('.button_group button', $toolbar).click(function(event){
+ event.preventDefault();
+
+ var params = eval("(" + $(this).attr('data-ui-action-params') + ")");
+
+ scriptletCenter.callInteractive({
+ action: $(this).attr('data-ui-action'),
+ context: options.actionContext,
+ extra: params
+ });
+ });
+
+ $toolbar.children().filter('select').change(function(event){
+ var slug = $(this).val();
+ $container.scrollLeft(0);
+ $('*[data-group]').hide().filter('[data-group=' + slug + ']').show();
+ }).change();
+
+ $('button.next', $toolbar).click(function() {
+ var $current_group = $('.button_group:visible', $toolbar);
+ var scroll = $container.scrollLeft();
+
+ var $hidden = $("li", $current_group).filter(function() {
+ return ($(this).position().left + $(this).outerWidth()) > $container.width();
+ }).first();
+
+ if($hidden.length > 0) {
+ scroll = $hidden.position().left + scroll + $hidden.outerWidth() - $container.width() + 1;
+ $container.scrollLeft(scroll);
+ };
+ });
+
+ $('button.prev', $toolbar).click(function() {
+ var $current_group = $('.button_group:visible', $toolbar);
+ var scroll = $container.scrollLeft();
+
+ /* first not visible element is: */
+ var $hidden = $("li", $current_group).filter(function() {
+ return $(this).position().left < 0;
+ }).last();
+
+ if( $hidden.length > 0)
+ {
+ scroll = scroll + $hidden.position().left;
+ $container.scrollLeft(scroll);
+ };
+ });
+
+ };
+
+})(jQuery);
\ No newline at end of file
--- /dev/null
+(function($){
+
+ function MotifsPerspective(options){
+
+ var old_callback = options.callback;
+
+ options.callback = function(){
+ var self = this;
+
+ self.$tag_name = $('#motifs-editor #tag-name');
+ withThemes(function(canonThemes){
+ self.$tag_name.autocomplete(canonThemes, {
+ autoFill: true,
+ multiple: true,
+ selectFirst: true,
+ highlight: false
+ });
+ })
+
+ self.$objects_list = $('#motifs-editor #objects-list');
+
+ self.x1 = null;
+ self.x2 = null;
+ self.y1 = null;
+ self.y2 = null;
+
+ if (!CurrentDocument.readonly) {
+ self.ias = $('#motifs-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
+ $('#motifs-editor #add').click(self._addObject(self));
+
+ $('.delete', self.$objects_list).live('click', function() {
+ $(this).prev().trigger('click');
+ if (window.confirm("Czy na pewno chcesz usunąć ten motyw?")) {
+ $(this).prev().remove();
+ $(this).remove();
+ }
+ self._resetSelection();
+ return false;
+ });
+ }
+
+ $('.image-object', self.$objects_list).live('click', function(){
+ $('.active', self.$objects_list).removeClass('active');
+ $(this).addClass('active');
+ var coords = $(this).data('coords');
+ if (coords) {
+ self.ias.setSelection.apply(self.ias, coords);
+ self.ias.setOptions({ show: true });
+ }
+ else {
+ self._resetSelection();
+ }
+ });
+
+ old_callback.call(this);
+ };
+
+ $.wiki.Perspective.call(this, options);
+ };
+
+ MotifsPerspective.prototype = new $.wiki.Perspective();
+
+ MotifsPerspective.prototype.freezeState = function(){
+
+ };
+
+ MotifsPerspective.prototype._resetSelection = function() {
+ var self = this;
+ self.x1 = self.x2 = self.y1 = self.y2 = null;
+ self.ias.setOptions({ hide: true });
+ }
+
+
+ MotifsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
+ var $e = $('<span class="image-object"></span>')
+ $e.text(name);
+ if (x1 !== null)
+ $e.data('coords', [x1, y1, x2, y2]);
+ this.$objects_list.append($e);
+ this.$objects_list.append('<span class="delete">(x)</span>');
+ }
+
+
+ MotifsPerspective.prototype._addObject = function(self) {
+ return function() {
+ outputs = [];
+ chunks = self.$tag_name.val().split(',');
+ for (i in chunks) {
+ item = chunks[i].trim();
+ if (item == '')
+ continue;
+ outputs.push(item.trim());
+ }
+ output = outputs.join(', ');
+
+ self._push(output, self.x1, self.y1, self.x2, self.y2);
+ self._resetSelection();
+ }
+ }
+
+ MotifsPerspective.prototype._fillCoords = function(self) {
+ return function(img, selection) {
+ $('.active', self.$objects_list).removeClass('active');
+ if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
+ self.x1 = selection.x1;
+ self.x2 = selection.x2;
+ self.y1 = selection.y1;
+ self.y2 = selection.y2;
+ }
+ else {
+ self.x1 = self.x2 = self.y1 = self.y2 = null;
+ }
+ }
+ }
+
+ MotifsPerspective.prototype.onEnter = function(success, failure){
+ var self = this;
+ this.$objects_list.children().remove();
+
+ $.each(this.doc.getImageItems('motyw'), function(i, e) {
+ self._push.apply(self, e);
+ });
+
+ if (this.x1 !== null)
+ this.ias.setOptions({enable: true, show: true});
+ else
+ this.ias.setOptions({enable: true});
+
+ $.wiki.Perspective.prototype.onEnter.call(this);
+
+ };
+
+ MotifsPerspective.prototype.onExit = function(success, failure){
+ var self = this;
+ var motifs = [];
+ this.$objects_list.children(".image-object").each(function(i, e) {
+ var args = $(e).data('coords');
+ if (!args)
+ args = [null, null, null, null];
+ args.unshift($(e).text());
+ motifs.push(args);
+ })
+ self.doc.setImageItems('motyw', motifs);
+
+ this.ias.setOptions({disable: true, hide: true});
+
+ };
+
+ $.wiki.MotifsPerspective = MotifsPerspective;
+
+})(jQuery);
--- /dev/null
+(function($){
+
+ function ObjectsPerspective(options){
+
+ var old_callback = options.callback;
+
+ options.callback = function(){
+ var self = this;
+
+ self.$tag_name = $('#objects-editor #tag-name');
+ self.$objects_list = $('#objects-editor #objects-list');
+
+ self.x1 = null;
+ self.x2 = null;
+ self.y1 = null;
+ self.y2 = null;
+
+ if (!CurrentDocument.readonly) {
+ self.ias = $('#objects-editor img.area-selectable').imgAreaSelect({ handles: true, onSelectEnd: self._fillCoords(self), instance: true });
+ $('#objects-editor #add').click(self._addObject(self));
+
+ $('.delete', self.$objects_list).live('click', function() {
+ $(this).prev().trigger('click');
+ if (window.confirm("Czy na pewno chcesz usunąć ten obiekt?")) {
+ $(this).prev().remove();
+ $(this).remove();
+ }
+ self._resetSelection();
+ return false;
+ });
+ }
+
+ $('.image-object', self.$objects_list).live('click', function(){
+ $('.active', self.$objects_list).removeClass('active');
+ $(this).addClass('active');
+ var coords = $(this).data('coords');
+ if (coords) {
+ self.ias.setSelection.apply(self.ias, coords);
+ self.ias.setOptions({ show: true });
+ }
+ else {
+ self._resetSelection();
+ }
+ });
+
+ old_callback.call(this);
+ };
+
+ $.wiki.Perspective.call(this, options);
+ };
+
+ ObjectsPerspective.prototype = new $.wiki.Perspective();
+
+ ObjectsPerspective.prototype.freezeState = function(){
+
+ };
+
+ ObjectsPerspective.prototype._resetSelection = function() {
+ var self = this;
+ self.x1 = self.x2 = self.y1 = self.y2 = null;
+ self.ias.setOptions({ hide: true });
+ }
+
+
+ ObjectsPerspective.prototype._push = function(name, x1, y1, x2, y2) {
+ var $e = $('<span class="image-object"></span>')
+ $e.text(name);
+ if (x1 !== null)
+ $e.data('coords', [x1, y1, x2, y2]);
+ this.$objects_list.append($e);
+ this.$objects_list.append('<span class="delete">(x)</span>');
+ }
+
+
+ ObjectsPerspective.prototype._addObject = function(self) {
+ return function() {
+ outputs = [];
+ chunks = self.$tag_name.val().split(',');
+ for (i in chunks) {
+ item = chunks[i].trim();
+ if (item == '')
+ continue;
+ outputs.push(item.trim());
+ }
+ output = outputs.join(', ');
+
+ self._push(output, self.x1, self.y1, self.x2, self.y2);
+ self._resetSelection();
+ }
+ }
+
+ ObjectsPerspective.prototype._fillCoords = function(self) {
+ return function(img, selection) {
+ $('.active', self.$objects_list).removeClass('active');
+ if (selection.x1 != selection.x2 && selection.y1 != selection.y2) {
+ self.x1 = selection.x1;
+ self.x2 = selection.x2;
+ self.y1 = selection.y1;
+ self.y2 = selection.y2;
+ }
+ else {
+ self.x1 = self.x2 = self.y1 = self.y2 = null;
+ }
+ }
+ }
+
+ ObjectsPerspective.prototype.onEnter = function(success, failure){
+ var self = this;
+ this.$objects_list.children().remove();
+
+ $.each(this.doc.getImageItems('obiekt'), function(i, e) {
+ self._push.apply(self, e);
+ });
+
+ if (this.x1 !== null)
+ this.ias.setOptions({enable: true, show: true});
+ else
+ this.ias.setOptions({enable: true});
+
+ $.wiki.Perspective.prototype.onEnter.call(this);
+
+ };
+
+ ObjectsPerspective.prototype.onExit = function(success, failure){
+ var self = this;
+ var objects = [];
+ this.$objects_list.children(".image-object").each(function(i, e) {
+ var args = $(e).data('coords');
+ if (!args)
+ args = [null, null, null, null];
+ args.unshift($(e).text());
+ objects.push(args);
+ })
+ self.doc.setImageItems('obiekt', objects);
+
+ this.ias.setOptions({disable: true, hide: true});
+
+ };
+
+ $.wiki.ObjectsPerspective = ObjectsPerspective;
+
+})(jQuery);
--- /dev/null
+/* COMMENT */
+(function($) {
+
+ function CodeMirrorPerspective(options)
+ {
+ var old_callback = options.callback;
+ options.callback = function(){
+ var self = this;
+
+ this.codemirror = CodeMirror.fromTextArea('codemirror_placeholder', {
+ parserfile: 'parsexml.js',
+ path: STATIC_URL + "js/lib/codemirror-0.8/",
+ stylesheet: STATIC_URL + "css/xmlcolors_20100906.css",
+ parserConfig: {
+ useHTMLKludges: false
+ },
+ iframeClass: 'xml-iframe',
+ textWrapping: true,
+ lineNumbers: true,
+ width: "100%",
+ height: "100%",
+ tabMode: 'spaces',
+ indentUnit: 0,
+ readOnly: CurrentDocument.readonly || false,
+ initCallback: function(){
+
+ self.codemirror.grabKeys(function(event) {
+ if (event.button) {
+ $(event.button).trigger('click');
+ event.button = null;
+ }
+ }, function(keycode, event) {
+ if(!event.altKey)
+ return false;
+
+ var c = String.fromCharCode(keycode).toLowerCase();
+ var button = $("#source-editor button[data-ui-accesskey='"+c+"']");
+ if(button.length == 0)
+ return false;
+
+ /* it doesn't matter which button we pick - all do the same */
+ event.button = button[0];
+ return true;
+ });
+
+ $('#source-editor .toolbar').toolbarize({
+ actionContext: self.codemirror
+ });
+
+ console.log("Initialized CodeMirror");
+
+ // textarea is no longer needed
+ $('codemirror_placeholder').remove();
+
+ old_callback.call(self);
+ }
+ });
+ };
+
+ $.wiki.Perspective.call(this, options);
+ };
+
+
+ CodeMirrorPerspective.prototype = new $.wiki.Perspective();
+
+ CodeMirrorPerspective.prototype.freezeState = function() {
+ this.config().position = this.codemirror.win.scrollY || 0;
+ };
+
+ CodeMirrorPerspective.prototype.unfreezeState = function () {
+ this.codemirror.win.scroll(0, this.config().position || 0);
+ };
+
+ CodeMirrorPerspective.prototype.onEnter = function(success, failure) {
+ $.wiki.Perspective.prototype.onEnter.call(this);
+
+ console.log('Entering', this.doc);
+ this.codemirror.setCode(this.doc.text);
+
+ /* fix line numbers bar */
+ var $nums = $('.CodeMirror-line-numbers');
+ var barWidth = $nums.width();
+
+ $(this.codemirror.frame.contentDocument.body).css('padding-left', barWidth);
+ // $nums.css('left', -barWidth);
+
+ $(window).resize();
+ this.unfreezeState(this._uistate);
+
+ if(success) success();
+ }
+
+ CodeMirrorPerspective.prototype.onExit = function(success, failure) {
+ this.freezeState();
+
+ $.wiki.Perspective.prototype.onExit.call(this);
+ console.log('Exiting', this.doc);
+ this.doc.setText(this.codemirror.getCode());
+
+ if ($('.vsplitbar').hasClass('active') && $('#SearchPerspective').hasClass('active')) {
+ $.wiki.switchToTab('#ScanGalleryPerspective');
+ }
+
+ if(success) success();
+ }
+
+ $.wiki.CodeMirrorPerspective = CodeMirrorPerspective;
+
+})(jQuery);
--- /dev/null
+(function($){
+
+ function SummaryPerspective(options) {
+ var old_callback = options.callback;
+ var self = this;
+
+ $.wiki.Perspective.call(this, options);
+ };
+
+ SummaryPerspective.prototype = new $.wiki.Perspective();
+
+ SummaryPerspective.prototype.freezeState = function(){
+ // must
+ };
+
+ SummaryPerspective.prototype.onEnter = function(success, failure){
+ $.wiki.Perspective.prototype.onEnter.call(this);
+
+ console.log("Entered summery view");
+ };
+
+ $.wiki.SummaryPerspective = SummaryPerspective;
+
+})(jQuery);
+
--- /dev/null
+(function($) {
+ $.wikiapi = {};
+ var noop = function() {
+ };
+ var noops = {
+ success: noop,
+ failure: noop
+ };
+ /*
+ * Return absolute reverse path of given named view. (at least he have it
+ * hard-coded in one place)
+ *
+ * TODO: think of a way, not to hard-code it here ;)
+ *
+ */
+ function reverse() {
+ var vname = arguments[0];
+ var base_path = "/images";
+
+ if (vname == "ajax_document_text") {
+ return base_path + "/text/" + arguments[1] + "/";
+ }
+
+ if (vname == "ajax_document_history") {
+
+ return base_path + "/history/" + arguments[1] + "/";
+ }
+*/
+/*
+ if (vname == "ajax_document_diff")
+ return base_path + "/" + arguments[1] + "/diff";
+
+ if (vname == "ajax_document_rev")
+ return base_path + "/" + arguments[1] + "/rev";
+
+ if (vname == "ajax_document_addtag")
+ return base_path + "/" + arguments[1] + "/tags";
+
+ if (vname == "ajax_publish")
+ return base_path + "/" + arguments[1] + "/publish";*/
+
+ console.log("Couldn't reverse match:", vname);
+ return "/404.html";
+ };
+
+ /*
+ * Document Abstraction
+ */
+ function WikiDocument(element_id) {
+ var meta = $('#' + element_id);
+ this.id = meta.attr('data-object-id');
+
+ this.revision = $("*[data-key='revision']", meta).text();
+ this.readonly = !!$("*[data-key='readonly']", meta).text();
+
+ this.text = null;
+ this.has_local_changes = false;
+ this._lock = -1;
+ this._context_lock = -1;
+ this._lock_count = 0;
+ };
+
+ WikiDocument.prototype.triggerDocumentChanged = function() {
+ $(document).trigger('wlapi_document_changed', this);
+ };
+ /*
+ * Fetch text of this document.
+ */
+ WikiDocument.prototype.fetch = function(params) {
+ params = $.extend({}, noops, params);
+ var self = this;
+ $.ajax({
+ method: "GET",
+ url: reverse("ajax_document_text", self.id),
+ data: {"commit": self.commit},
+ dataType: 'json',
+ success: function(data) {
+ var changed = false;
+
+ if (self.text === null || self.commit !== data.commit) {
+ self.text = data.text;
+ if (self.text === '') {
+ self.text = '<obraz></obraz>';
+ }
+ self.revision = data.revision;
+ self.commit = data.commit;
+ changed = true;
+ self.triggerDocumentChanged();
+ };
+
+ self.has_local_changes = false;
+ params['success'](self, changed);
+ },
+ error: function() {
+ params['failure'](self, "Nie udało się wczytać treści dokumentu.");
+ }
+ });
+ };
+ /*
+ * Fetch history of this document.
+ *
+ * from - First revision to fetch (default = 0) upto - Last revision to
+ * fetch (default = tip)
+ *
+ */
+ WikiDocument.prototype.fetchHistory = function(params) {
+ /* this doesn't modify anything, so no locks */
+ params = $.extend({}, noops, params);
+ var self = this;
+ $.ajax({
+ method: "GET",
+ url: reverse("ajax_document_history", self.id),
+ dataType: 'json',
+ data: {
+ "from": params['from'],
+ "upto": params['upto']
+ },
+ success: function(data) {
+ params['success'](self, data);
+ },
+ error: function() {
+ params['failure'](self, "Nie udało się wczytać historii dokumentu.");
+ }
+ });
+ };
+
+ /*
+ * Set document's text
+ */
+ WikiDocument.prototype.setText = function(text) {
+ this.text = text;
+ this.has_local_changes = true;
+ };
+
+ /*
+ * Save text back to the server
+ */
+ WikiDocument.prototype.save = function(params) {
+ params = $.extend({}, noops, params);
+ var self = this;
+
+ if (!self.has_local_changes) {
+ console.log("Abort: no changes.");
+ return params['success'](self, false, "Nie ma zmian do zapisania.");
+ };
+
+ // Serialize form to dictionary
+ var data = {};
+ $.each(params['form'].serializeArray(), function() {
+ data[this.name] = this.value;
+ });
+
+ data['textsave-text'] = self.text;
+
+ $.ajax({
+ url: reverse("ajax_document_text", self.id),
+ type: "POST",
+ dataType: "json",
+ data: data,
+ success: function(data) {
+ var changed = false;
+
+ $('#header').removeClass('saving');
+
+ if (data.text) {
+ self.text = data.text;
+ self.revision = data.revision;
+ self.commit = data.commit;
+ changed = true;
+ self.triggerDocumentChanged();
+ };
+
+ params['success'](self, changed, ((changed && "Udało się zapisać :)") || "Twoja wersja i serwera jest identyczna"));
+ },
+ error: function(xhr) {
+ if ($('#header').hasClass('saving')) {
+ $('#header').removeClass('saving');
+ $.blockUI({
+ message: "<p>Nie udało się zapisać zmian. <br/><button onclick='$.unblockUI()'>OK</button></p>"
+ })
+ }
+ else {
+ try {
+ params['failure'](self, $.parseJSON(xhr.responseText));
+ }
+ catch (e) {
+ params['failure'](self, {
+ "__message": "<p>Nie udało się zapisać - błąd serwera.</p>"
+ });
+ };
+ }
+
+ }
+ });
+
+ $('#save-hide').click(function(){
+ $('#header').addClass('saving');
+ $.unblockUI();
+ $.wiki.blocking.unblock();
+ });
+ }; /* end of save() */
+
+ WikiDocument.prototype.publish = function(params) {
+ params = $.extend({}, noops, params);
+ var self = this;
+ $.ajax({
+ url: reverse("ajax_publish", self.id),
+ type: "POST",
+ dataType: "json",
+ success: function(data) {
+ params.success(self, data);
+ },
+ error: function(xhr) {
+ if (xhr.status == 403 || xhr.status == 401) {
+ params.failure(self, "Nie masz uprawnień lub nie jesteś zalogowany.");
+ }
+ else {
+ try {
+ params.failure(self, xhr.responseText);
+ }
+ catch (e) {
+ params.failure(self, "Nie udało się - błąd serwera.");
+ };
+ };
+
+ }
+ });
+ };
+ WikiDocument.prototype.setTag = function(params) {
+ params = $.extend({}, noops, params);
+ var self = this;
+ var data = {
+ "addtag-id": self.id,
+ };
+
+ /* unpack form */
+ $.each(params.form.serializeArray(), function() {
+ data[this.name] = this.value;
+ });
+
+ $.ajax({
+ url: reverse("ajax_document_addtag", self.id),
+ type: "POST",
+ dataType: "json",
+ data: data,
+ success: function(data) {
+ params.success(self, data.message);
+ },
+ error: function(xhr) {
+ if (xhr.status == 403 || xhr.status == 401) {
+ params.failure(self, {
+ "__all__": ["Nie masz uprawnień lub nie jesteś zalogowany."]
+ });
+ }
+ else {
+ try {
+ params.failure(self, $.parseJSON(xhr.responseText));
+ }
+ catch (e) {
+ params.failure(self, {
+ "__all__": ["Nie udało się - błąd serwera."]
+ });
+ };
+ };
+ }
+ });
+ };
+
+ WikiDocument.prototype.getImageItems = function(tag) {
+ var self = this;
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(self.text, 'text/xml');
+ var error = $('parsererror', doc);
+
+ if (error.length != 0) {
+ return null;
+ }
+
+ var a = [];
+ $(tag, doc).each(function(i, e) {
+ var $e = $(e);
+ a.push([
+ $e.text(),
+ $e.attr('x1'),
+ $e.attr('y1'),
+ $e.attr('x2'),
+ $e.attr('y2')
+ ]);
+ });
+
+ return a;
+ }
+
+ WikiDocument.prototype.setImageItems = function(tag, items) {
+ var self = this;
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(self.text, 'text/xml');
+ var serializer = new XMLSerializer();
+ var error = $('parsererror', doc);
+
+ if (error.length != 0) {
+ return null;
+ }
+
+ $(tag, doc).remove();
+ $root = $(doc.firstChild);
+ $.each(items, function(i, e) {
+ var el = $(doc.createElement(tag));
+ el.text(e[0]);
+ if (e[1] !== null) {
+ el.attr('x1', e[1]);
+ el.attr('y1', e[2]);
+ el.attr('x2', e[3]);
+ el.attr('y2', e[4]);
+ }
+ $root.append(el);
+ });
+ self.setText(serializer.serializeToString(doc));
+ }
+
+
+ $.wikiapi.WikiDocument = WikiDocument;
+})(jQuery);
+
+
+
+// Wykonuje block z załadowanymi kanonicznymi motywami
+function withThemes(code_block, onError)
+{
+ if (typeof withThemes.canon == 'undefined') {
+ $.ajax({
+ url: '/editor/themes',
+ dataType: 'text',
+ success: function(data) {
+ withThemes.canon = data.split('\n');
+ code_block(withThemes.canon);
+ },
+ error: function() {
+ withThemes.canon = null;
+ code_block(withThemes.canon);
+ }
+ })
+ }
+ else {
+ code_block(withThemes.canon);
+ }
+}
+
url(r'^documents/', include('catalogue.urls')),
url(r'^apiclient/', include('apiclient.urls')),
url(r'^editor/', include('wiki.urls')),
+ url(r'^images/', include('wiki_img.urls')),
# Static files (should be served by Apache)
url(r'^%s(?P<path>.+)$' % settings.MEDIA_URL[1:], 'django.views.static.serve',