Commit fcc466d5 by benjaoming

Image management, revert, delete and restore

parent c1455967
......@@ -35,14 +35,13 @@ class ArticleQuerySet(QuerySet):
def active(self):
return self.filter(current_revision__deleted=False)
class ArticleFkQuerySet(QuerySet):
def can_read(self, user):
"""Filter objects so only the ones with a user's reading access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
return self
if user.is_anonymous():
q = self.filter(article__other_read=True)
else:
......@@ -56,7 +55,7 @@ class ArticleFkQuerySet(QuerySet):
"""Filter objects so only the ones with a user's writing access
are included"""
if user.has_perm('wiki.moderator'):
return self.get_query_set()
return self
if user.is_anonymous():
q = self.filter(article__other_write=True)
else:
......
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'ImageRevision.width'
db.add_column('wiki_imagerevision', 'width',
self.gf('django.db.models.fields.SmallIntegerField')(default=0),
keep_default=False)
# Adding field 'ImageRevision.height'
db.add_column('wiki_imagerevision', 'height',
self.gf('django.db.models.fields.SmallIntegerField')(default=0),
keep_default=False)
def backwards(self, orm):
# Deleting field 'ImageRevision.width'
db.delete_column('wiki_imagerevision', 'width')
# Deleting field 'ImageRevision.height'
db.delete_column('wiki_imagerevision', 'height')
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'})
},
'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'})
},
'django_notify.notificationtype': {
'Meta': {'object_name': 'NotificationType', 'db_table': "'notify_notificationtype'"},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128', 'primary_key': 'True'}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True', 'blank': 'True'})
},
'django_notify.settings': {
'Meta': {'object_name': 'Settings', 'db_table': "'notify_settings'"},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'interval': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'django_notify.subscription': {
'Meta': {'object_name': 'Subscription', 'db_table': "'notify_subscription'"},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'notification_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.NotificationType']"}),
'object_id': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True', 'blank': 'True'}),
'send_emails': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'settings': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['django_notify.Settings']"})
},
'sites.site': {
'Meta': {'ordering': "('domain',)", 'object_name': 'Site', 'db_table': "'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'wiki.article': {
'Meta': {'object_name': 'Article'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'current_set'", 'unique': 'True', 'null': 'True', 'to': "orm['wiki.ArticleRevision']"}),
'group': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'null': 'True', 'blank': 'True'}),
'group_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'group_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'other_read': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'other_write': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_articles'", 'null': 'True', 'to': "orm['auth.User']"})
},
'wiki.articleforobject': {
'Meta': {'unique_together': "(('content_type', 'object_id'),)", 'object_name': 'ArticleForObject'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'content_type_set_for_articleforobject'", 'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_mptt': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {})
},
'wiki.articleplugin': {
'Meta': {'object_name': 'ArticlePlugin'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'wiki.articlerevision': {
'Meta': {'ordering': "('created',)", 'unique_together': "(('article', 'revision_number'),)", 'object_name': 'ArticleRevision'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'content': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.ArticleRevision']", 'null': 'True', 'blank': 'True'}),
'revision_number': ('django.db.models.fields.IntegerField', [], {}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'wiki.articlesubscription': {
'Meta': {'object_name': 'ArticleSubscription', '_ormbases': ['wiki.ArticlePlugin', 'django_notify.Subscription']},
'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
'subscription_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['django_notify.Subscription']", 'unique': 'True'})
},
'wiki.attachment': {
'Meta': {'object_name': 'Attachment', '_ormbases': ['wiki.ReusablePlugin']},
'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'current_set'", 'unique': 'True', 'null': 'True', 'to': "orm['wiki.AttachmentRevision']"}),
'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}),
'reusableplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ReusablePlugin']", 'unique': 'True', 'primary_key': 'True'})
},
'wiki.attachmentrevision': {
'Meta': {'ordering': "('created',)", 'object_name': 'AttachmentRevision'},
'attachment': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Attachment']"}),
'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.AttachmentRevision']", 'null': 'True', 'blank': 'True'}),
'revision_number': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'wiki.image': {
'Meta': {'object_name': 'Image', '_ormbases': ['wiki.RevisionPlugin']},
'revisionplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.RevisionPlugin']", 'unique': 'True', 'primary_key': 'True'})
},
'wiki.imagerevision': {
'Meta': {'object_name': 'ImageRevision', '_ormbases': ['wiki.RevisionPluginRevision']},
'height': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '2000'}),
'revisionpluginrevision_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.RevisionPluginRevision']", 'unique': 'True', 'primary_key': 'True'}),
'width': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'})
},
'wiki.reusableplugin': {
'Meta': {'object_name': 'ReusablePlugin', '_ormbases': ['wiki.ArticlePlugin']},
'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
'articles': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'shared_plugins_set'", 'symmetrical': 'False', 'to': "orm['wiki.Article']"})
},
'wiki.revisionplugin': {
'Meta': {'object_name': 'RevisionPlugin', '_ormbases': ['wiki.ArticlePlugin']},
'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'}),
'current_revision': ('django.db.models.fields.related.OneToOneField', [], {'blank': 'True', 'related_name': "'plugin_set'", 'unique': 'True', 'null': 'True', 'to': "orm['wiki.RevisionPluginRevision']"})
},
'wiki.revisionpluginrevision': {
'Meta': {'object_name': 'RevisionPluginRevision'},
'automatic_log': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}),
'locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'plugin': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revision_set'", 'to': "orm['wiki.RevisionPlugin']"}),
'previous_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.RevisionPluginRevision']", 'null': 'True', 'blank': 'True'}),
'revision_number': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}),
'user_message': ('django.db.models.fields.TextField', [], {'blank': 'True'})
},
'wiki.simpleplugin': {
'Meta': {'object_name': 'SimplePlugin', '_ormbases': ['wiki.ArticlePlugin']},
'article_revision': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.ArticleRevision']"}),
'articleplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wiki.ArticlePlugin']", 'unique': 'True', 'primary_key': 'True'})
},
'wiki.urlpath': {
'Meta': {'unique_together': "(('site', 'parent', 'slug'),)", 'object_name': 'URLPath'},
'article': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Article']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'parent': ('mptt.fields.TreeForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['wiki.URLPath']"}),
'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'site': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['sites.Site']"}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'})
}
}
complete_apps = ['wiki']
\ No newline at end of file
......@@ -153,9 +153,9 @@ class RevisionPlugin(ArticlePlugin):
# Permissions... overwrite if necessary
def can_read(self, **kwargs):
return self.article.can_read(self, **kwargs)
return self.article.can_read(**kwargs)
def can_write(self, **kwargs):
return self.article.can_write(self, **kwargs)
return self.article.can_write(**kwargs)
def add_revision(self, new_revision, save=True):
"""
......@@ -217,6 +217,7 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
class Meta:
app_label = settings.APP_LABEL
get_latest_by = ('revision_number',)
ordering = ('-created',)
######################################################
# SIGNAL HANDLERS
......@@ -233,6 +234,6 @@ def update_simple_plugins(instance, *args, **kwargs):
if kwargs.get('created', False):
p_revisions = SimplePlugin.objects.filter(article=instance.article, deleted=False)
# TODO: This was breaking things. SimplePlugin doesn't have a revision?
#p_revisions.update(revision=instance)
p_revisions.update(article_revision=instance)
signals.post_save.connect(update_simple_plugins, ArticleRevision)
\ No newline at end of file
......@@ -236,7 +236,7 @@ class AttachmentSearchView(ArticleMixin, ListView):
def get_queryset(self):
self.query = self.request.GET.get('query', None)
if not self.query:
qs = models.Attachment.objects.active().get_empty_query_set()
qs = models.Attachment.objects.get_empty_query_set()
else:
qs = models.Attachment.objects.active().can_read(self.request.user)
qs = qs.filter(Q(original_filename__contains=self.query) |
......
......@@ -15,7 +15,7 @@ def upload_path(instance, filename):
# Has to match original extension filename
upload_path = settings.UPLOAD_PATH
upload_path = upload_path.replace('%aid', str(instance.image.article.id))
upload_path = upload_path.replace('%aid', str(instance.plugin.image.article.id))
if settings.UPLOAD_PATH_OBSCURIFY:
import random, hashlib
m=hashlib.md5(str(random.randint(0,100000000000000)))
......@@ -45,8 +45,12 @@ class Image(RevisionPlugin):
class ImageRevision(RevisionPluginRevision):
image = models.ImageField(upload_to=upload_path,
max_length=2000)
max_length=2000, height_field='height',
width_field='width')
width = models.SmallIntegerField(default=0)
height = models.SmallIntegerField(default=0)
def get_filename(self):
if self.image:
return self.image.path.split('/')[-1]
......@@ -54,11 +58,25 @@ class ImageRevision(RevisionPluginRevision):
def get_size(self):
"""Used to retrieve the file size and not cause exceptions."""
try:
return self.file.size
return self.image.size
except ValueError:
return None
def inherit_predecessor(self, image):
"""
Inherit certain properties from predecessor because it's very
convenient. Remember to always call this method before
setting properties :)"""
predecessor = image.current_revision.imagerevision
self.plugin = predecessor.plugin
self.image = predecessor.image
self.width = predecessor.width
self.height = predecessor.height
self.deleted = predecessor.deleted
self.locked = predecessor.locked
class Meta:
verbose_name = _(u'image revision')
verbose_name_plural = _(u'image revisions')
app_label = settings.APP_LABEL
ordering = ('-created',)
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load wiki_tags i18n humanize thumbnail %}
{% load url from future %}
{% block pagetitle %}{% trans "Images" %}: {{ article.current_revision.title }}{% endblock %}
......@@ -7,34 +7,94 @@
{% block wiki_contents_tab %}
<p class="lead">{% trans "The following images are available for this article. Copy the markdown tag to directly refer to an image from the article text." %}</p>
<p>
<a href="{% url 'wiki:edit' path=urlpath.path article_id=article.id %}" class="btn btn-large">
<a href="{% url 'wiki:edit' path=urlpath.path article_id=article.id %}">
<span class="icon-arrow-left"></span>
{% trans "Back to edit page" %}
</a>
</p>
<table class="table table-bordered table-striped" style="width: 100%;">
<tr>
<th>{% trans "Image" %}</th>
<th>{% trans "Markdown tag" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
</tr>
{% for image in images %}
{% with image.current_revision.imagerevision as revision %}
<tr>
<th>{{ revision.get_filename }}</th>
<th>{% trans "Markdown tag" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
</tr>
<tr>
<td rowspan="3">
{% thumbnail revision.image "250x150" crop="center" as thumb %}
<p class="thumbnail">
<img src="{{ thumb.url }}" alt="{{ revision.get_filename }}" />
</p>
{% endthumbnail %}
{% if image|can_write:user %}
<p>
<a href="#">
<span class="icon-upload"></span>
{% trans "Upload new file" %}
</a> <br />
{% if revision.deleted %}
<a href="{% url 'wiki:images_restore' path=urlpath.path article_id=article.id image_id=image.id %}">
<span class="icon-repeat"></span>
{% trans "Restore image" %}
</a>
{% else %}
<a href="{% url 'wiki:images_delete' path=urlpath.path article_id=article.id image_id=image.id %}">
<span class="icon-remove"></span>
{% trans "Remove image" %}
</a>
{% endif %}
</p>
{% endif %}
</td>
<td><code>[image:{{ image.id }}]</code></td>
<td>
{% if image.current_revision.user %}{{ image.current_revision.user }}{% else %}{% if user|is_moderator %}{{ image.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
<p>{% include "wiki/includes/revision_info.html" %}</p>
</td>
<td>{{ revision.get_size|filesizeformat }}<br />{{ revision.width }}x{{ revision.height }} pixels
</td>
</tr>
<tr>
<th colspan="100">{% trans "History" %}</th>
</tr>
<tr>
<td colspan="100">
<table class="table">
{% for old_revision in image.revision_set.all %}
<tr>
<td>
{% thumbnail revision.imagerevision.image "50x50" crop="center" as thumb %}
<img src="{{ thumb.url }}" alt="{{ revision.imagerevision.get_filename }}" />
{% endthumbnail %}
</td>
<td>{% include "wiki/includes/revision_info.html" with current_revision=image.current_revision revision=old_revision %}</td>
<td>{{ old_revision.imagerevision.get_size|filesizeformat }}</td>
<td>{{ old_revision.imagerevision.width }}x{{ old_revision.imagerevision.height }}</td>
<td>
{% if image|can_write:user and old_revision != image.current_revision %}
<a href="#">
<span class="icon-refresh"></span>
{% trans "Revert to this version" %}
</a>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</td>
<td>{{ image.current_revision.get_size|filesizeformat }}</td>
</tr>
{% endwith %}
{% empty %}
<tr>
<td colspan="100">
<p style="margin-bottom: 20px;"><em>{% trans "There are no images for this article." %}</em></p>
</td>
{% endfor %}
{% endfor %}
</table>
{% include "wiki/includes/pagination.html" %}
{% endblock %}
{% load i18n wiki_tags wiki_images_tags humanize thumbnail %}
{% load url from future %}
<h4>{% trans "Available images" %}</h4>
<p>{% trans "Click on an image below to insert in your text. The format of the code inserted is:" %}<br /><code>[image:id alignment caption text]</code></p>
<div style="max-height: 300px; overflow: auto;">
<table class="table table-striped">
{% for image in article|images_for_article %}
{% thumbnail image.image "150x50" crop="center" as thumb %}
{% with article|images_for_article as images %}
<style type="text/css">
#image-list tr:first-child td {border:0;}
</style>
<div style="max-height: 300px; overflow: scroll-y;">
<table class="table" style="margin-bottom: 0;" id="image-list">
{% for image in images %}
{% with image.current_revision.imagerevision as revision %}
{% thumbnail revision.image "50x50" crop="center" as thumb %}
<tr>
<td>
<div class="thumbnail">
<img src="{{ thumb.url }}" alt="{{ image.get_filename }}" />
</div>
<td style="white-space: nowrap;">
<p>{{ revision.get_filename|truncatechars:30 }}</p>
<p>
<a href=""><span class="icon-edit"></span> {% trans "Insert" %}</a> <br />
<a href=""><span class="icon-edit"></span> {% trans "Replace" %}</a>
</p>
</td>
<td>
<span class="label">
{% trans "uploaded" %} {{ image.created|naturaltime }}
</span>
<div class="thumbnail">
<img src="{{ thumb.url }}" alt="{{ revision.get_filename }}" />
</div>
</td>
</tr>
{% endthumbnail %}
{% endwith %}
{% empty %}
<tr>
<td><em>{% trans "No images found for this article" %}</em></td>
......@@ -30,12 +34,13 @@
</table>
</div>
{% if images %}
<p>
<a href="{% url 'wiki:images_index' path=urlpath.path article_id=article.id %}">
{% trans "Manage images" %} &raquo;
</a>
</p>
{% endif %}
<hr />
<h4>{% trans "Add new image" %}</h4>
......@@ -85,3 +90,14 @@
<p><em>{% trans "You do not have permissions to add images." %}</em></p>
{% endif %}
{% endif %}
<hr />
<h4>
{% trans "How to use images" %}
</h4>
<p>{% trans "After uploading an image, it is attached to this particular artice and can be used only here. Other users may replace the image, but older versions are kept. You probably want to show the image with a nice caption. To achieve this, press the Insert button and fill in the caption fields and possibly choose to have you image floating right or left of the content. You can use Markdown in the caption. The markdown code syntax for images looks like this:" %}<br /><code>[image:id alignment caption text]</code></p>
{% endwith %}
......@@ -7,7 +7,7 @@ register = template.Library()
@register.filter
def images_for_article(article):
return models.ImageRevision.objects.filter(plugin__article=article).order_by('-created')
return models.Image.objects.filter(article=article).order_by('-current_revision__created')
@register.filter
def images_can_add(article, user):
......
from django.views.generic.list import ListView
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.views.generic.base import RedirectView
from django.views.generic.list import ListView
from wiki.decorators import get_article
from wiki.views.mixins import ArticleMixin
from wiki.plugins.images import models
from wiki.views.mixins import ArticleMixin
class ImageView(ArticleMixin, ListView):
......@@ -18,9 +22,66 @@ class ImageView(ArticleMixin, ListView):
return super(ImageView, self).dispatch(request, article, *args, **kwargs)
def get_queryset(self):
return models.Image.objects.filter(article=self.article)
if (self.request.user.has_perm('wiki.moderator') or
self.article.owner == self.request.user):
images = models.Image.objects.filter(article=self.article)
else:
images = models.Image.objects.filter(article=self.article,
current_revision__deleted=False)
images.select_related()
return images
def get_context_data(self, **kwargs):
kwargs.update(ArticleMixin.get_context_data(self, **kwargs))
return ListView.get_context_data(self, **kwargs)
\ No newline at end of file
class DeleteView(ArticleMixin, RedirectView):
permanent = False
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs):
self.image = get_object_or_404(models.Image, article=article,
id=kwargs.get('image_id', None))
self.restore = kwargs.get('restore', False)
return ArticleMixin.dispatch(self, request, article, *args, **kwargs)
def get_redirect_url(self, **kwargs):
new_revision = models.ImageRevision()
new_revision.inherit_predecessor(self.image)
new_revision.set_from_request(self.request)
new_revision.deleted = not self.restore
new_revision.save()
self.image.current_revision = new_revision
self.image.save()
if self.restore:
messages.info(self.request, _('%s has been marked as deleted') % new_revision.get_filename())
else:
messages.info(self.request, _('%s has been restored') % new_revision.get_filename())
if self.urlpath:
return reverse('wiki:images_index', kwargs={'path': self.urlpath.path})
return reverse('wiki:images_index', kwargs={'article_id': self.article.id})
class RevisionChangeView(ArticleMixin, RedirectView):
permanent = False
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs):
self.image = get_object_or_404(models.Image, article=article,
id=kwargs.get('image_id', None))
self.revision = get_object_or_404(models.ImageRevision, plugin__article=article,
id=kwargs.get('rev_id', None))
return ArticleMixin.dispatch(self, request, article, *args, **kwargs)
def get_redirect_url(self, **kwargs):
self.image.current_revision = self.revision
self.image.save()
messages.info(self.request, _('%(file)s has been changed to revision #%(revision)d') %
{'file': self.image.current_revision.imagerevision.get_filename(),
'revision': self.revision.revision_number})
if self.urlpath:
return reverse('wiki:images_index', kwargs={'path': self.urlpath.path})
return reverse('wiki:images_index', kwargs={'article_id': self.article.id})
......@@ -25,7 +25,7 @@ class ImagePlugin(BasePlugin):
# List of notifications to construct signal handlers for. This
# is handled inside the notifications plugin.
notifications = [{'model': models.Image,
'message': lambda obj: _(u"An image was added: %s") % obj.get_filename(),
'message': lambda obj: _(u"An image was added: %s") % obj.current_revision.get_filename(),
'key': ARTICLE_EDIT,
'created': True,
'get_article': lambda obj: obj.article}
......@@ -33,6 +33,9 @@ class ImagePlugin(BasePlugin):
urlpatterns = patterns('',
url('^$', views.ImageView.as_view(), name='images_index'),
url('^delete/(?P<image_id>\d+)/$', views.DeleteView.as_view(), name='images_delete'),
url('^restore/(?P<image_id>\d+)/$', views.DeleteView.as_view(), name='images_restore', kwargs={'restore': True}),
url('^restore/(?P<image_id>\d+)/set-revision/(?P<rev_id>\d+)/$', views.RevisionChangeView.as_view(), name='images_restore'),
)
#markdown_extensions = [AttachmentExtension()]
......
......@@ -16,8 +16,8 @@
#id_title {font-size: 20px; height: 30px; padding: 6px; width: 98%;}
#id_summary {width: 98%; padding: 6px;}
#article_edit_form label {max-width: 120px;}
#article_edit_form .controls {margin-left: 140px;}
#article_edit_form label {max-width: 100px;}
#article_edit_form .controls {margin-left: 120px;}
.form-horizontal label { font-size: 18px; font-weight: bold; color: #777;}
#settings_form label {font-size: inherit; font-weight: normal;}
......
......@@ -6,17 +6,32 @@
{% block wiki_contents_tab %}
<div style="width: 67%; min-width: 600px; float: left;">
<div class="row-fluid">
<div class="span8">
<form method="POST" class="form-horizontal" id="article_edit_form">
<input type="hidden" name="save" value="1" />
<input type="hidden" name="preview" value="1" />
{% with edit_form as form %}
{% include "wiki/includes/editor.html" %}
{% endwith %}
</form>
</div>
<div class="span4">
{% include "wiki/includes/editor_sidebar.html" %}
</div>
</div>
<div style="clear: both"></div>
<div class="form-actions">
<button type="submit" name="preview" value="1" class="btn btn-large" onclick="$('#previewModal').modal('show'); this.form.target='previewWindow'; this.form.action='{% url 'wiki:preview' path=urlpath.path article_id=article.id %}'">
<a class="btn btn-large" onclick="$('#previewModal').modal('show'); document.getElementById('article_edit_form').target='previewWindow'; document.getElementById('article_edit_form').action='{% url 'wiki:preview' path=urlpath.path article_id=article.id %}'; $('#article_edit_form').submit()" href="#">
<span class="icon-eye-open"></span>
{% trans "Preview" %}
</button>
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
</a>
<a class="btn btn-large btn-primary" onclick="document.getElementById('article_edit_form').target=''; document.getElementById('article_edit_form').action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'; $('#article_edit_form').submit();" href="#">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
......@@ -36,23 +51,12 @@
<span class="icon-circle-arrow-left"></span>
{% trans "Back to editor" %}
</a>
<button type="submit" name="save" value="1" class="btn btn-large btn-primary" onclick="this.form.target=''; this.form.action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'">
<a class="btn btn-large btn-primary" onclick="document.getElementById('article_edit_form').target=''; document.getElementById('article_edit_form').action='{% url 'wiki:edit' path=urlpath.path article_id=article.id %}'; document.getElementById('article_edit_form').submit();" href="#">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</a>
</div>
</div>
</form>
</div>
<div style="width: 33%; min-width: 300px; float: right;">
<div style="padding-left: 40px;">
{% include "wiki/includes/editor_sidebar.html" %}
</div>
</div>
<div style="clear: both"></div>
{% endblock %}
......@@ -60,16 +60,7 @@
<div class="accordion-group">
<div class="accordion-heading">
<a class="accordion-toggle" style="float: left;" href="#collapse{{ revision.revision_number }}" onclick="get_diff_json('{% url 'wiki:diff' revision.id %}', $('#collapse{{ revision.revision_number }}'))">
{{ revision.created }} (#{{ revision.revision_number }}) by {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% if revision == article.current_revision %}
<strong>*</strong>
{% endif %}
{% if revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %}
{% if revision.previous_revision.deleted and not revision.deleted %}
<span class="badge badge-success">{% trans "restored" %}</span>
{% endif %}
{% include "wiki/includes/revision_info.html" with current_revision=article.current_revision %}
<div style="color: #CCC;">
<small>
{% if revision.user_message %}
......
{% comment %}
This reusable code is shared between different templates and different inheritors of
BaseRevision.
{% endcomment %}
{% load wiki_tags i18n %}
{{ revision.created }} (#{{ revision.revision_number }}) {% trans "by" %} {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% if revision == current_revision %}
<strong>*</strong>
{% endif %}
{% if revision.deleted %}
<span class="badge badge-important">{% trans "deleted" %}</span>
{% endif %}
{% if revision.previous_revision.deleted and not revision.deleted %}
<span class="badge badge-success">{% trans "restored" %}</span>
{% endif %}
......@@ -5,16 +5,17 @@
{% if revision %}
<div class="alert alert-info">
<strong>{% trans "Previewing revision" %}:</strong> {{ revision.created }} (#{{ revision.revision_number }}) by {% if revision.user %}{{ revision.user }}{% else %}{% if user|is_moderator %}{{ revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
<strong>{% trans "Previewing revision" %}:</strong>
{% include "wiki/includes/revision_info.html" %}
</div>
{% endif %}
{% if merge %}
<div class="alert alert-info">
<strong>{% trans "Previewing merge between" %}:</strong>
{{ merge1.created }} (#{{ merge1.revision_number }}) by {% if merge1.user %}{{ merge1.user }}{% else %}{% if user|is_moderator %}{{ merge1.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% include "wiki/includes/revision_info.html" with revision=merge1 %}
<strong>{% trans "and" %}</strong>
{{ merge1.created }} (#{{ merge1.revision_number }}) by {% if merge1.user %}{{ merge1.user }}{% else %}{% if user|is_moderator %}{{ merge1.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
{% include "wiki/includes/revision_info.html" with revision=merge2 %}
</div>
{% endif %}
......
......@@ -59,7 +59,7 @@ def can_read(obj, user):
@register.filter
def can_write(obj, user):
"""Articles and plugins have a can_write method..."""
return obj.can_write(user=user)
return obj.can_write(**{'user': user})
@register.filter
def is_moderator(user):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment