Commit 45b67b27 by benjaoming

Adding RevisionPlugin. Images plugin becoming a plugin with its own revision…

Adding RevisionPlugin. Images plugin becoming a plugin with its own revision system. Fixing anonymous settings for attachments plugin.
parent d58dcbc7
...@@ -9,9 +9,11 @@ ADMINS = ( ...@@ -9,9 +9,11 @@ ADMINS = (
# ('Your Name', 'your_email@example.com'), # ('Your Name', 'your_email@example.com'),
) )
from django.core.urlresolvers import reverse_lazy #from django.core.urlresolvers import reverse_lazy
#LOGIN_REDIRECT_URL = reverse_lazy('wiki:get', kwargs={'path': ''})
LOGIN_REDIRECT_URL = reverse_lazy('wiki:get', kwargs={'path': ''}) # This forces the wiki login view to redirect to the referer...
LOGIN_REDIRECT_URL = None
LOGIN_URL = '/_accounts/login/' LOGIN_URL = '/_accounts/login/'
LOGOUT_URL = '/_accounts/logout/' LOGOUT_URL = '/_accounts/logout/'
...@@ -140,3 +142,5 @@ LOGGING = { ...@@ -140,3 +142,5 @@ LOGGING = {
}, },
} }
} }
WIKI_ANONYMOUS_WRITE = True
\ No newline at end of file
...@@ -4,11 +4,7 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -4,11 +4,7 @@ from django.utils.translation import ugettext_lazy as _
from mptt.admin import MPTTModelAdmin from mptt.admin import MPTTModelAdmin
from django import forms from django import forms
from django.forms.widgets import HiddenInput
from django.core.urlresolvers import get_callable
import models import models
from conf import settings
import editors import editors
class ArticleObjectAdmin(GenericTabularInline): class ArticleObjectAdmin(GenericTabularInline):
...@@ -56,7 +52,7 @@ class ArticleForm(forms.ModelForm): ...@@ -56,7 +52,7 @@ class ArticleForm(forms.ModelForm):
self.fields['current_revision'].queryset = revisions self.fields['current_revision'].queryset = revisions
else: else:
self.fields['current_revision'].queryset = models.ArticleRevision.objects.get_empty_query_set() self.fields['current_revision'].queryset = models.ArticleRevision.objects.get_empty_query_set()
self.fields['current_revision'].widget = HiddenInput() self.fields['current_revision'].widget = forms.HiddenInput()
class ArticleAdmin(admin.ModelAdmin): class ArticleAdmin(admin.ModelAdmin):
inlines = [ArticleRevisionInline] inlines = [ArticleRevisionInline]
......
...@@ -102,7 +102,7 @@ class Article(models.Model): ...@@ -102,7 +102,7 @@ class Article(models.Model):
for decendant in self.decendant_objects(): for decendant in self.decendant_objects():
if decendant.INHERIT_PERMISSIONS: if decendant.INHERIT_PERMISSIONS:
decendant.owner = self.owner decendant.owner = self.owner
def add_revision(self, new_revision, save=True): def add_revision(self, new_revision, save=True):
""" """
Sets the properties of a revision and ensures its the current Sets the properties of a revision and ensures its the current
......
...@@ -65,14 +65,14 @@ class ReusablePlugin(ArticlePlugin): ...@@ -65,14 +65,14 @@ class ReusablePlugin(ArticlePlugin):
articles = models.ManyToManyField(Article, related_name='shared_plugins_set') articles = models.ManyToManyField(Article, related_name='shared_plugins_set')
# Permission methods - you may override these, if they don't fit your logic. # Permission methods - you may override these, if they don't fit your logic.
def can_read(self, *args, **kwargs): def can_read(self, **kwargs):
if self.article: if self.article:
return self.article.can_read(*args, **kwargs) return self.article.can_read(**kwargs)
return False return False
def can_write(self, *args, **kwargs): def can_write(self, **kwargs):
if self.article: if self.article:
return self.article.can_write(*args, **kwargs) return self.article.can_write(**kwargs)
return False return False
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
...@@ -151,9 +151,36 @@ class RevisionPlugin(ArticlePlugin): ...@@ -151,9 +151,36 @@ class RevisionPlugin(ArticlePlugin):
'If you need to do a roll-back, simply change the value of this field.'), 'If you need to do a roll-back, simply change the value of this field.'),
) )
# Permissions... overwrite if necessary
def can_read(self, **kwargs):
return self.article.can_read(self, **kwargs)
def can_write(self, **kwargs):
return self.article.can_write(self, **kwargs)
def add_revision(self, new_revision, save=True):
"""
Sets the properties of a revision and ensures its the current
revision.
"""
assert self.id or save, ('RevisionPluginRevision.add_revision: Sorry, you cannot add a'
'revision to a plugin that has not been saved '
'without using save=True')
if not self.id: self.save()
revisions = self.revision_set.all()
try:
new_revision.revision_number = revisions.latest().revision_number + 1
except RevisionPluginRevision.DoesNotExist:
new_revision.revision_number = 0
new_revision.plugin = self
new_revision.previous_revision = self.current_revision
if save: new_revision.save()
self.current_revision = new_revision
if save: self.save()
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
class RevisionPluginRevision(BaseRevisionMixin, models.Model): class RevisionPluginRevision(BaseRevisionMixin, models.Model):
""" """
If you want your plugin to maintain revisions, make an extra model If you want your plugin to maintain revisions, make an extra model
...@@ -162,7 +189,7 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model): ...@@ -162,7 +189,7 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
(this class is very much copied from wiki.models.article.ArticleRevision (this class is very much copied from wiki.models.article.ArticleRevision
""" """
plugin = models.ForeignKey(RevisionPlugin) plugin = models.ForeignKey(RevisionPlugin, related_name='revision_set')
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if (not self.id and if (not self.id and
...@@ -175,9 +202,9 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model): ...@@ -175,9 +202,9 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
if not self.revision_number: if not self.revision_number:
try: try:
previous_revision = self.article.articlerevision_set.latest() previous_revision = self.plugin.revision_set.latest()
self.revision_number = previous_revision.revision_number + 1 self.revision_number = previous_revision.revision_number + 1
except ArticleRevision.DoesNotExist: except RevisionPluginRevision.DoesNotExist:
self.revision_number = 1 self.revision_number = 1
super(RevisionPluginRevision, self).save(*args, **kwargs) super(RevisionPluginRevision, self).save(*args, **kwargs)
...@@ -187,29 +214,9 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model): ...@@ -187,29 +214,9 @@ class RevisionPluginRevision(BaseRevisionMixin, models.Model):
self.plugin.current_revision = self self.plugin.current_revision = self
self.plugin.save() self.plugin.save()
def add_revision(self, new_revision, save=True):
"""
Sets the properties of a revision and ensures its the current
revision.
"""
assert self.id or save, ('RevisionPluginRevision.add_revision: Sorry, you cannot add a'
'revision to a plugin that has not been saved '
'without using save=True')
if not self.id: self.save()
revisions = self.pluginrevision_set.all()
try:
new_revision.revision_number = revisions.latest().revision_number + 1
except RevisionPlugin.DoesNotExist:
new_revision.revision_number = 0
new_revision.plugin = self
new_revision.previous_revision = self.current_revision
if save: new_revision.save()
self.current_revision = new_revision
if save: self.save()
class Meta: class Meta:
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
get_latest_by = ('revision_number',)
###################################################### ######################################################
# SIGNAL HANDLERS # SIGNAL HANDLERS
......
...@@ -23,6 +23,12 @@ class Attachment(ReusablePlugin): ...@@ -23,6 +23,12 @@ class Attachment(ReusablePlugin):
original_filename = models.CharField(max_length=256, verbose_name=_(u'original filename'), blank=True, null=True) original_filename = models.CharField(max_length=256, verbose_name=_(u'original filename'), blank=True, null=True)
def can_write(self, **kwargs):
user = kwargs.get('user', None)
if not settings.ANONYMOUS and (not user or user.is_anonymous()):
return False
return ReusablePlugin.can_write(self, **kwargs)
class Meta: class Meta:
verbose_name = _(u'attachment') verbose_name = _(u'attachment')
verbose_name_plural = _(u'attachments') verbose_name_plural = _(u'attachments')
......
...@@ -11,6 +11,7 @@ from django.views.generic.base import TemplateView, View ...@@ -11,6 +11,7 @@ from django.views.generic.base import TemplateView, View
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from django.views.generic.list import ListView from django.views.generic.list import ListView
from wiki.conf import settings as wiki_settings
from wiki.core.http import send_file from wiki.core.http import send_file
from wiki.decorators import get_article from wiki.decorators import get_article
from wiki.plugins.attachments import models, settings, forms from wiki.plugins.attachments import models, settings, forms
...@@ -94,6 +95,8 @@ class AttachmentReplaceView(ArticleMixin, FormView): ...@@ -94,6 +95,8 @@ class AttachmentReplaceView(ArticleMixin, FormView):
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, attachment_id, *args, **kwargs): def dispatch(self, request, article, attachment_id, *args, **kwargs):
self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article) self.attachment = get_object_or_404(models.Attachment.objects.active(), id=attachment_id, articles=article)
if not self.attachment.can_write(user=request.user):
return redirect(wiki_settings.LOGIN_URL)
return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs) return super(AttachmentReplaceView, self).dispatch(request, article, *args, **kwargs)
def form_valid(self, form): def form_valid(self, form):
......
from django.contrib import admin from django.contrib import admin
from django import forms
import models import models
class ImageForm(forms.ModelForm):
class Meta:
model = models.Image
def __init__(self, *args, **kwargs):
super(ImageForm, self).__init__(*args, **kwargs)
if self.instance.pk:
revisions = models.ImageRevision.objects.filter(plugin=self.instance)
self.fields['current_revision'].queryset = revisions
else:
self.fields['current_revision'].queryset = models.ImageRevision.objects.get_empty_query_set()
self.fields['current_revision'].widget = forms.HiddenInput()
class ImageRevisionInline(admin.TabularInline): class ImageRevisionInline(admin.TabularInline):
model = models.ImageRevision model = models.ImageRevision
extra = 1 extra = 1
fields = ('image', 'locked', 'deleted') fields = ('image', 'locked', 'deleted')
class ImageAdmin(admin.ModelAdmin): class ImageAdmin(admin.ModelAdmin):
form = ImageForm
inlines = (ImageRevisionInline,) inlines = (ImageRevisionInline,)
admin.site.register(models.Image, ImageAdmin) admin.site.register(models.Image, ImageAdmin)
\ No newline at end of file
...@@ -7,13 +7,24 @@ from wiki.plugins.images import models ...@@ -7,13 +7,24 @@ from wiki.plugins.images import models
class SidebarForm(forms.ModelForm, PluginSidebarFormMixin): class SidebarForm(forms.ModelForm, PluginSidebarFormMixin):
def __init__(self, *args, **kwargs): def __init__(self, article, user, *args, **kwargs):
self.article = kwargs.pop('article') self.article = article
self.user = user
super(SidebarForm, self).__init__(*args, **kwargs) super(SidebarForm, self).__init__(*args, **kwargs)
def get_usermessage(self): def get_usermessage(self):
return _(u"New image %s was successfully uploaded. You can use it by selecting it from the list of available images.") % self.instance.get_filename() return _(u"New image %s was successfully uploaded. You can use it by selecting it from the list of available images.") % self.instance.get_filename()
def save(self, *args, **kwargs):
if not self.instance.id:
image = models.Image()
image.article = self.article
kwargs['commit'] = False
revision = super(SidebarForm, self).save(*args, **kwargs)
image.add_revision(self.instance, save=True)
return revision
return super(SidebarForm, self).save(*args, **kwargs)
class Meta: class Meta:
model = models.ImageRevision model = models.ImageRevision
fields = ('image',) fields = ('image',)
\ No newline at end of file
...@@ -10,28 +10,54 @@ from wiki.models.pluginbase import RevisionPlugin, RevisionPluginRevision ...@@ -10,28 +10,54 @@ from wiki.models.pluginbase import RevisionPlugin, RevisionPluginRevision
if not "sorl.thumbnail" in django_settings.INSTALLED_APPS: if not "sorl.thumbnail" in django_settings.INSTALLED_APPS:
raise ImproperlyConfigured('wiki.plugins.images: needs sorl.thumbnail in INSTALLED_APPS') raise ImproperlyConfigured('wiki.plugins.images: needs sorl.thumbnail in INSTALLED_APPS')
def upload_path(instance, filename):
from os import path
# Has to match original extension filename
upload_path = settings.UPLOAD_PATH
upload_path = upload_path.replace('%aid', str(instance.image.article.id))
if settings.UPLOAD_PATH_OBSCURIFY:
import random, hashlib
m=hashlib.md5(str(random.randint(0,100000000000000)))
upload_path = path.join(upload_path, m.hexdigest())
return path.join(upload_path, filename)
class Image(RevisionPlugin): class Image(RevisionPlugin):
# The plugin system is so awesome that the inheritor doesn't need to do # The plugin system is so awesome that the inheritor doesn't need to do
# anything! :D # anything! :D
def can_write(self, **kwargs):
user = kwargs.get('user', None)
if not settings.ANONYMOUS and (not user or user.is_anonymous()):
return False
return RevisionPlugin.can_write(self, **kwargs)
class Meta: class Meta:
verbose_name = _(u'image') verbose_name = _(u'image')
verbose_name_plural = _(u'images') verbose_name_plural = _(u'images')
app_label = settings.APP_LABEL app_label = settings.APP_LABEL
def __unicode__(self): def __unicode__(self):
return _(u'Image: %s') % self.current_revision.get_filename() title = (_(u'Image: %s') % self.current_revision.imagerevision.get_filename()) if self.current_revision else _(u'Current revision not set!!')
return unicode(title)
class ImageRevision(RevisionPluginRevision): class ImageRevision(RevisionPluginRevision):
image = models.ImageField(upload_to=settings.IMAGE_PATH, image = models.ImageField(upload_to=upload_path,
max_length=2000) max_length=2000)
def get_filename(self): def get_filename(self):
if self.image: if self.image:
return self.image.path.split('/')[-1] return self.image.path.split('/')[-1]
def get_size(self):
"""Used to retrieve the file size and not cause exceptions."""
try:
return self.file.size
except ValueError:
return None
class Meta: class Meta:
verbose_name = _(u'image revision') verbose_name = _(u'image revision')
verbose_name_plural = _(u'image revisions') verbose_name_plural = _(u'image revisions')
......
from django.conf import settings as django_settings from django.conf import settings as django_settings
# Where to store images # Where to store images
IMAGE_PATH = getattr(django_settings, 'WIKI_IMAGE_PATH', "wiki/images/%aid/") UPLOAD_PATH = getattr(django_settings, 'WIKI_IMAGE_PATH', "wiki/images/%aid/")
# Should the upload path be obscurified? If so, a random hash will be added to the path
# such that someone can not guess the location of files (if you have
# restricted permissions and the files are still located within the web server's
UPLOAD_PATH_OBSCURIFY = getattr(django_settings, 'WIKI_UPLOAD_PATH_OBSCURIFY', True)
# Allow anonymous users to upload (not nice on an open network) # Allow anonymous users to upload (not nice on an open network)
ANONYMOUS = getattr(django_settings, 'WIKI_ATTACHMENTS_ANONYMOUS', False) ANONYMOUS = getattr(django_settings, 'WIKI_ATTACHMENTS_ANONYMOUS', False)
......
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Images" %}: {{ article.current_revision.title }}{% endblock %}
{% 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">
<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 %}
<tr>
<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 %}
</td>
<td>{{ image.current_revision.get_size|filesizeformat }}</td>
</tr>
{% empty %}
<tr>
<td colspan="100">
<p style="margin-bottom: 20px;"><em>{% trans "There are no images for this article." %}</em></p>
</td>
{% endfor %}
</table>
{% endblock %}
...@@ -5,57 +5,83 @@ ...@@ -5,57 +5,83 @@
<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> <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>
<table class="table table-striped"> <div style="max-height: 300px; overflow: auto;">
{% for image in article|images_for_article %} <table class="table table-striped">
<tr> {% for image in article|images_for_article %}
<td> {% thumbnail image.image "150x50" crop="center" as thumb %}
{% thumbnail image.image "100x50" crop="center" as thumb %} <tr>
<img src="{{ thumb.url }}" alt="{{ image.get_filename }}" width="{{ thumb.width }}" height="{{ thumb.height }}" /> <td>
<div class="thumbnail">
<img src="{{ thumb.url }}" alt="{{ image.get_filename }}" />
</div>
</td>
<td>
<span class="label">
{% trans "uploaded" %} {{ image.created|naturaltime }}
</span>
</td>
</tr>
{% endthumbnail %} {% endthumbnail %}
</td> {% empty %}
<td><span class="label">{{ image.created|naturaltime }}</span></td> <tr>
</tr> <td><em>{% trans "No images found for this article" %}</em></td>
{% endfor %} </tr>
</table> {% endfor %}
</table>
</div>
<p>
<a href="{% url 'wiki:images_index' path=urlpath.path article_id=article.id %}">
{% trans "Manage images" %} &raquo;
</a>
</p>
{% if article|can_write:user %}
<hr /> <hr />
<h4>{% trans "Add new image" %}</h4> <h4>{% trans "Add new image" %}</h4>
{% if form.non_field_errors %} {% if article|images_can_add:user %}
{% if form_error_title %}<h4 class="alert-heading">{{ form_error_title }}</h4>{% endif %} {% if form.non_field_errors %}
{% for error_message in form.non_field_errors %} {% if form_error_title %}<h4 class="alert-heading">{{ form_error_title }}</h4>{% endif %}
<div class="alert alert-block alert-error"> {% for error_message in form.non_field_errors %}
{{ error_message }} <div class="alert alert-block alert-error">
</div> {{ error_message }}
{% endfor %} </div>
{% endif %}
{% for field in form %}
<p id="div_{{ field.auto_id }}" class="fields {% if field.errors %} error{% endif %}">
{% if field.label %}
<!--<label for="{{ field.id_for_label }}" class="{% if field.field.required %}requiredField{% endif %}">
{{ field.label|safe }}
</label>-->
{% endif %}
{{ field }}
{% if field.errors %}
{% for error in field.errors %}
<div id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block"><strong>{{ error }}</strong></div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
</p>
{% if field.help_text %}
<p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<p> {% for field in form %}
<button type="submit" name="{{ plugin.slug }}_save" value="1" class="btn btn-large"> <fieldset id="div_{{ field.auto_id }}" class="control-group fields {% if field.errors %} error{% endif %}">
<span class="icon-upload"></span> {% if field.label %}
{% trans "Add image" %} <!--<label for="{{ field.id_for_label }}" class="{% if field.field.required %}requiredField{% endif %}">
</button> {{ field.label|safe }}
</p> </label>-->
{% endif %}
{{ field }}
{% if field.errors %}
<div id="error_{{ forloop.counter }}_{{ field.auto_id }}" class="help-block">
{% for error in field.errors %}
<div>{{ error }}</div>
{% endfor %}
</div>
{% endif %}
</fieldset>
{% if field.help_text %}
<p id="hint_{{ field.auto_id }}" class="help-block">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<p>
<button type="submit" name="{{ plugin.slug }}_save" value="1" class="btn btn-large">
<span class="icon-upload"></span>
{% trans "Add image" %}
</button>
</p>
{% else %}
{% if user.is_anonymous %}
{% include "wiki/includes/anonymous_blocked.html" %}
{% else %}
<p><em>{% trans "You do not have permissions to add images." %}</em></p>
{% endif %}
{% endif %} {% endif %}
from django import template from django import template
from wiki.plugins.images import models from wiki.plugins.images import models
from wiki.plugins.images import settings
register = template.Library() register = template.Library()
@register.filter @register.filter
def images_for_article(article): def images_for_article(article):
return models.Image.objects.filter(article=article).order_by('-created') return models.ImageRevision.objects.filter(plugin__article=article).order_by('-created')
@register.filter
def images_can_add(article, user):
if not settings.ANONYMOUS and (not user or user.is_anonymous()):
return False
return article.can_write(user)
\ No newline at end of file
from django.views.generic.base import TemplateView from django.views.generic.list import ListView
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from wiki.decorators import get_article from wiki.decorators import get_article
from wiki.views.mixins import ArticleMixin from wiki.views.mixins import ArticleMixin
class ImageView(ArticleMixin, TemplateView): from wiki.plugins.images import models
class ImageView(ArticleMixin, ListView):
template_name = 'wiki/plugins/images/index.html'
allow_empty = True
context_object_name = 'images'
paginate_by = 10
@method_decorator(get_article(can_read=True)) @method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
return super(ImageView, self).dispatch(request, article, *args, **kwargs) return super(ImageView, self).dispatch(request, article, *args, **kwargs)
def get_queryset(self):
return models.Image.objects.filter(article=self.article)
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
...@@ -28,9 +28,13 @@ class ImagePlugin(plugins.BasePlugin): ...@@ -28,9 +28,13 @@ class ImagePlugin(plugins.BasePlugin):
'message': lambda obj: _(u"An image was added: %s") % obj.get_filename(), 'message': lambda obj: _(u"An image was added: %s") % obj.get_filename(),
'key': ARTICLE_EDIT, 'key': ARTICLE_EDIT,
'created': True, 'created': True,
'get_article': lambda obj: obj.revision.article} 'get_article': lambda obj: obj.article}
] ]
urlpatterns = patterns('',
url('^$', views.ImageView.as_view(), name='images_index'),
)
#markdown_extensions = [AttachmentExtension()] #markdown_extensions = [AttachmentExtension()]
def __init__(self): def __init__(self):
......
...@@ -14,9 +14,13 @@ class SubscriptionForm(PluginSettingsFormMixin, forms.Form): ...@@ -14,9 +14,13 @@ class SubscriptionForm(PluginSettingsFormMixin, forms.Form):
settings_order = 1 settings_order = 1
settings_write_access = False settings_write_access = False
edit = forms.BooleanField(required=False, label=_(u'When this article is edited'))
edit_email = forms.BooleanField(required=False, label=_(u'Also receive emails about article edits'),
widget=forms.CheckboxInput(attrs={'onclick': mark_safe("$('#id_edit').attr('checked', $(this).is(':checked'));")}))
def __init__(self, article, user, *args, **kwargs): def __init__(self, article, user, *args, **kwargs):
# This has to be here to avoid unresolved imports in wiki_plugins # This has to be here to avoid unresolved imports in wiki_plugins
import models from wiki.plugins.notifications import models
self.article = article self.article = article
self.user = user self.user = user
initial = kwargs.pop('initial', None) initial = kwargs.pop('initial', None)
...@@ -32,10 +36,6 @@ class SubscriptionForm(PluginSettingsFormMixin, forms.Form): ...@@ -32,10 +36,6 @@ class SubscriptionForm(PluginSettingsFormMixin, forms.Form):
kwargs['initial'] = initial kwargs['initial'] = initial
super(SubscriptionForm, self).__init__(*args, **kwargs) super(SubscriptionForm, self).__init__(*args, **kwargs)
edit = forms.BooleanField(required=False, label=_(u'When this article is edited'))
edit_email = forms.BooleanField(required=False, label=_(u'Also receive emails about article edits'),
widget=forms.CheckboxInput(attrs={'onclick': mark_safe("$('#id_edit').attr('checked', $(this).is(':checked'));")}))
def get_usermessage(self): def get_usermessage(self):
if self.changed_data: if self.changed_data:
return _('Your notification settings were updated.') return _('Your notification settings were updated.')
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
<div style="width: 67%; min-width: 600px; float: left;"> <div style="width: 67%; min-width: 600px; float: left;">
<form method="POST" class="form-horizontal" id="article_edit_form"> <form method="POST" class="form-horizontal" id="article_edit_form">
{% include "wiki/includes/editor.html" %} {% with edit_form as form %}
{% include "wiki/includes/editor.html" %}
{% endwith %}
<div class="form-actions"> <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 %}'"> <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 %}'">
<span class="icon-eye-open"></span> <span class="icon-eye-open"></span>
......
{% load wiki_tags i18n %} {% load wiki_tags i18n %}
{% include "wiki/includes/editormedia.html" %} {% include "wiki/includes/editormedia.html" %}
{% wiki_form edit_form %} {% wiki_form form %}
<script language="javascript"> <script language="javascript">
$(document).ready(function() { $(document).ready(function() {
$("#id_revision").val('{{ article.current_revision.id }}'); $("#id_revision").val('{{ article.current_revision.id }}');
......
{% for plugin in sidebar %} {% for plugin, plugin_form in sidebar %}
<div class="accordion" id="accordion_{{ plugin.slug }}"> <div class="accordion" id="accordion_{{ plugin.slug }}">
...@@ -10,12 +10,13 @@ ...@@ -10,12 +10,13 @@
</a> </a>
</div> </div>
<div id="collapse_{{ plugin.slug }}" class="accordion-body collapse{% if form_images.errors %} in{% endif %}"> <div id="collapse_{{ plugin.slug }}" class="accordion-body collapse{% if plugin_form.errors %} in{% endif %}">
<div class="accordion-inner form-vertical"> <div class="accordion-inner form-vertical">
{% if plugin.sidebar.template %} {% if plugin.sidebar.template %}
{% with form_images as form and plugin as plugin %} {% with plugin_form as form and plugin as plugin %}
<form method="POST" class="form-horizontal" enctype="multipart/form-data"> <form method="POST" class="form-horizontal" action="?f={{ plugin_form.form_id }}" enctype="multipart/form-data">
{% include plugin.sidebar.template %} {% csrf_token %}
{% include plugin.sidebar.template %}
</form> </form>
{% endwith %} {% endwith %}
{% endif %} {% endif %}
......
...@@ -54,12 +54,12 @@ def wiki_form(context, form_obj): ...@@ -54,12 +54,12 @@ def wiki_form(context, form_obj):
@register.filter @register.filter
def can_read(obj, user): def can_read(obj, user):
"""Articles and plugins have a can_read method...""" """Articles and plugins have a can_read method..."""
return obj.can_read(user) return obj.can_read(user=user)
@register.filter @register.filter
def can_write(obj, user): def can_write(obj, user):
"""Articles and plugins have a can_write method...""" """Articles and plugins have a can_write method..."""
return obj.can_write(user) return obj.can_write(user=user)
@register.filter @register.filter
def is_moderator(user): def is_moderator(user):
......
...@@ -36,17 +36,29 @@ class Login(FormView): ...@@ -36,17 +36,29 @@ class Login(FormView):
form_class = AuthenticationForm form_class = AuthenticationForm
template_name = "wiki/accounts/login.html" template_name = "wiki/accounts/login.html"
def get_form_kwargs(self): def get_form_kwargs(self):
self.request.session.set_test_cookie() self.request.session.set_test_cookie()
kwargs = super(Login, self).get_form_kwargs() kwargs = super(Login, self).get_form_kwargs()
kwargs['request'] = self.request kwargs['request'] = self.request
return kwargs return kwargs
def post(self, request, *args, **kwargs):
self.referer = request.session.get('login_referer', '')
return FormView.post(self, request, *args, **kwargs)
def get(self, request, *args, **kwargs):
self.referer = request.META.get('HTTP_REFERER', '')
request.session['login_referer'] = self.referer
return FormView.get(self, request, *args, **kwargs)
def form_valid(self, form, *args, **kwargs): def form_valid(self, form, *args, **kwargs):
auth_login(self.request, form.get_user()) auth_login(self.request, form.get_user())
messages.info(self.request, _(u"You are now logged in! Have fun!")) messages.info(self.request, _(u"You are now logged in! Have fun!"))
if self.request.GET.get("next", None): if self.request.GET.get("next", None):
return redirect(self.request.GET['next']) return redirect(self.request.GET['next'])
return redirect(django_settings.LOGIN_REDIRECT_URL) if django_settings.LOGIN_REDIRECT_URL:
return redirect(django_settings.LOGIN_REDIRECT_URL)
else:
return redirect(self.referer)
\ No newline at end of file
...@@ -216,6 +216,7 @@ class Delete(FormView, ArticleMixin): ...@@ -216,6 +216,7 @@ class Delete(FormView, ArticleMixin):
class Edit(FormView, ArticleMixin): class Edit(FormView, ArticleMixin):
"""Edit an article and process sidebar plugins."""
form_class = forms.EditForm form_class = forms.EditForm
template_name="wiki/edit.html" template_name="wiki/edit.html"
...@@ -223,54 +224,64 @@ class Edit(FormView, ArticleMixin): ...@@ -223,54 +224,64 @@ class Edit(FormView, ArticleMixin):
@method_decorator(get_article(can_write=True)) @method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs): def dispatch(self, request, article, *args, **kwargs):
self.sidebar_plugins = plugins_registry.get_sidebar() self.sidebar_plugins = plugins_registry.get_sidebar()
self.sidebar_forms = {} self.sidebar_forms = []
for plugin in self.sidebar_plugins:
sidebar_form_class = plugin.sidebar.get('form_class', None)
if sidebar_form_class:
form_kwargs = {}
form_kwargs['prefix'] = plugin.slug
form_kwargs['article'] = article
form_kwargs.update(plugin.sidebar['get_form_kwargs'](article))
plugin.sidebar_form_context = 'form_' + plugin.slug
if request.POST.get(plugin.slug+'_save', '') == '1':
form_kwargs['data'] = request.POST
form_kwargs['files'] = request.FILES
self.sidebar_forms[plugin.sidebar_form_context] = sidebar_form_class(**form_kwargs)
return super(Edit, self).dispatch(request, article, *args, **kwargs) return super(Edit, self).dispatch(request, article, *args, **kwargs)
def get_form(self, form_class): def get_form(self, form_class):
""" """
Returns an instance of the form to be used in this view. Checks from querystring data that the edit form is actually being saved,
otherwise removes the 'data' and 'files' kwargs from form initialisation.
""" """
kwargs = self.get_form_kwargs() kwargs = self.get_form_kwargs()
if self.request.POST.get('save', '') != '1': if self.request.POST.get('save', '') != '1':
kwargs['data'] = None
kwargs['files'] = None
kwargs['no_clean'] = True kwargs['no_clean'] = True
return form_class(self.article.current_revision, **kwargs) return form_class(self.article.current_revision, **kwargs)
def get_sidebar_form_classes(self):
"""Returns dictionary of form classes for the sidebar. If no form class is
specified, puts None in dictionary. Keys in the dictionary are used
to identify which form is being saved."""
form_classes = {}
for cnt, plugin in enumerate(self.sidebar_plugins):
form_classes['form%d' % cnt] = plugin.sidebar.get('form_class', None)
return form_classes
def get(self, request, *args, **kwargs):
# Generate sidebar forms
self.sidebar_forms = []
for form_id, Form in self.get_sidebar_form_classes().items():
form = Form(self.article, self.request.user)
setattr(form, 'form_id', form_id)
self.sidebar_forms.append(form)
return super(Edit, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
# Generate sidebar forms
# Check if any of the plugin form data is supposed to be saved self.sidebar_forms = []
for plugin in self.sidebar_plugins: for form_id, Form in self.get_sidebar_form_classes().items():
if self.request.POST.get(plugin.slug+'_save', '') == '1': if form_id == self.request.GET.get('f', None):
form = self.sidebar_forms[plugin.sidebar_form_context] form = Form(self.article, self.request.user, data=self.request.POST, files=self.request.FILES)
if form.is_valid(): if form.is_valid():
form.save() form.save()
message = form.get_usermessage() usermessage = form.get_usermessage()
if message: if usermessage:
messages.success(request, message) messages.success(self.request, usermessage)
#if self.urlpath: else:
# return redirect('wiki:edit', path=self.urlpath.path) messages.success(self.request, _(u'Your changes were saved.'))
#else: if self.urlpath:
# return redirect('wiki:edit', article_id=self.article.id) return redirect('wiki:edit', path=self.urlpath.path)
return redirect('wiki:edit', article_id=self.article.id)
if self.request.POST.get('save', '') == '1': else:
return super(Edit, self).post(request, *args, **kwargs) form = Form(self.article, self.request.user)
else: setattr(form, 'form_id', form_id)
return super(Edit, self).get(request, *args, **kwargs) self.sidebar_forms.append(form)
return super(Edit, self).post(request, *args, **kwargs)
def form_valid(self, form): def form_valid(self, form):
"""Create a new article revision when the edit form is valid
(does not concern any sidebar forms!)."""
revision = models.ArticleRevision() revision = models.ArticleRevision()
revision.inherit_predecessor(self.article) revision.inherit_predecessor(self.article)
revision.title = form.cleaned_data['title'] revision.title = form.cleaned_data['title']
...@@ -283,24 +294,22 @@ class Edit(FormView, ArticleMixin): ...@@ -283,24 +294,22 @@ class Edit(FormView, ArticleMixin):
return self.get_success_url() return self.get_success_url()
def get_success_url(self): def get_success_url(self):
"""Go to the article view page when the article has been saved"""
if self.urlpath: if self.urlpath:
return redirect("wiki:get", path=self.urlpath.path) return redirect("wiki:get", path=self.urlpath.path)
return redirect('wiki:get', article_id=self.article.id) return redirect('wiki:get', article_id=self.article.id)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['edit_form'] = kwargs.pop('form', None) kwargs['edit_form'] = kwargs.pop('form', None)
kwargs['editor'] = editors.editor kwargs['editor'] = editors.editor
kwargs['selected_tab'] = 'edit' kwargs['selected_tab'] = 'edit'
kwargs['sidebar'] = self.sidebar_plugins kwargs['sidebar'] = zip(self.sidebar_plugins, self.sidebar_forms)
kwargs.update(self.sidebar_forms)
return super(Edit, self).get_context_data(**kwargs) return super(Edit, self).get_context_data(**kwargs)
# TODO: ...
class Deleted(Delete): class Deleted(Delete):
"""Tell a user that an article has been deleted. If user has permissions,
let user restore and possibly purge the deleted article and children."""
template_name="wiki/deleted.html" template_name="wiki/deleted.html"
form_class = forms.DeleteForm form_class = forms.DeleteForm
...@@ -404,6 +413,9 @@ class Settings(ArticleMixin, TemplateView): ...@@ -404,6 +413,9 @@ class Settings(ArticleMixin, TemplateView):
settings_forms.append(self.permission_form_class) settings_forms.append(self.permission_form_class)
settings_forms.sort(key=lambda form: form.settings_order) settings_forms.sort(key=lambda form: form.settings_order)
for i in range(len(settings_forms)): for i in range(len(settings_forms)):
# TODO: Do not set an attribute on a form class - this
# could be mixed up with a different instance
# Use strategy from Edit view...
setattr(settings_forms[i], 'action', 'form%d' % i) setattr(settings_forms[i], 'action', 'form%d' % i)
return settings_forms return settings_forms
......
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