Commit 341db91d by Bridger Maxwell

Merge branch 'master' of git://github.com/benjaoming/django-wiki

parents 193d5149 846a665f
......@@ -16,6 +16,7 @@ Not implemented - will be ASAP
* South migrations **Soon**
* View source for read-only articles + locked status
* Global moderator permission **Almost done** (need to add grant form for users with *grant* permissions)
* Are you sure you wanna leave this page?
Ideas
=====
......
......@@ -11,6 +11,7 @@ from itertools import chain
from wiki import models
from wiki.editors import editor
from wiki.core.diff import simple_merge
from django.forms.widgets import HiddenInput
class CreateRoot(forms.Form):
......@@ -219,3 +220,14 @@ class PermissionsForm(forms.ModelForm):
class Meta:
model = models.Article
fields = ('group', 'group_read', 'group_write', 'other_read', 'other_write')
class DeleteForm(forms.Form):
confirm = forms.BooleanField(required=False)
purge = forms.BooleanField(widget=HiddenInput(), required=False)
def clean(self):
cd = self.cleaned_data
if not cd['confirm']:
raise forms.ValidationError(_(u'You are not sure enough!'))
......@@ -48,3 +48,25 @@ from wiki.core.plugins_load import load_wiki_plugins
load_wiki_plugins()
from django.core import urlresolvers
original_django_reverse = urlresolvers.reverse
def reverse(*args, **kwargs):
"""Now this is a crazy and silly hack, but it is basically here to
enforce that an empty path always takes precedence over an article_id
such that the root article doesn't get resolved to /ID/ but /."""
if args[0].startswith('wiki:'):
url_kwargs = kwargs.get('kwargs', {})
path = url_kwargs.get('path', False)
# If a path is supplied then discard the article_id
if path != False:
url_kwargs.pop('article_id', None)
url_kwargs['path'] = path
kwargs['kwargs'] = url_kwargs
return original_django_reverse(*args, **kwargs)
# Now we redefine reverse method
urlresolvers.reverse = reverse
\ No newline at end of file
......@@ -9,6 +9,7 @@ from django.utils.translation import ugettext_lazy as _
from wiki.conf import settings
from wiki.core import article_markdown, plugins_registry
from wiki import managers
from mptt.models import MPTTModel
class Article(models.Model):
......@@ -69,7 +70,7 @@ class Article(models.Model):
return False
def decendant_objects(self):
for obj in self.objectforarticle_set.filter(has_parent_field=True):
for obj in self.objectforarticle_set.filter(is_mptt=True):
for decendant in obj.get_decendants():
yield decendant
......@@ -115,11 +116,11 @@ class Article(models.Model):
def add_object_relation(self, obj):
content_type = ContentType.objects.get_for_model(obj)
has_parent_field = hasattr(obj, 'parent')
is_mptt = isinstance(obj, MPTTModel)
rel = ArticleForObject.objects.get_or_create(article=self,
content_type=content_type,
object_id=obj.id,
has_parent_method=has_parent_field)
is_mptt=is_mptt)
return rel
@classmethod
......@@ -159,7 +160,7 @@ class ArticleForObject(models.Model):
object_id = models.PositiveIntegerField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_id")
has_parent_method = models.BooleanField(default=False, editable=False)
is_mptt = models.BooleanField(default=False, editable=False)
class Meta:
app_label = settings.APP_LABEL
......@@ -168,7 +169,9 @@ class ArticleForObject(models.Model):
# Do not allow several objects
unique_together = ('content_type', 'object_id')
class BaseRevision(models.Model):
class BaseRevisionMixin(models.Model):
"""This is an abstract model used as a mixin: Do not override any of the
core model methods but respect the inheritor's freedom to do so itself."""
revision_number = models.IntegerField(editable=False, verbose_name=_(u'revision number'))
......@@ -200,11 +203,8 @@ class BaseRevision(models.Model):
class Meta:
abstract = True
app_label = settings.APP_LABEL
get_latest_by = ('revision_number',)
ordering = ('created',)
class ArticleRevision(BaseRevision):
class ArticleRevision(BaseRevisionMixin, models.Model):
"""This is where main revision data is stored. To make it easier to
copy, do NEVER create m2m relationships."""
......@@ -266,3 +266,10 @@ class ArticleRevision(BaseRevision):
self.article.save()
if not self.title:
self.title = self.article.title
class Meta:
app_label = settings.APP_LABEL
get_latest_by = ('revision_number',)
ordering = ('created',)
unique_together = ('article', 'revision_number')
# -*- coding: utf-8 -*-
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.db.models import signals
"""
There are two kinds of plugin objects:
......@@ -18,26 +19,36 @@ There are two kinds of plugin objects:
from article import Article, ArticleRevision
from wiki.conf import settings
class ArticlePlugin(models.Model):
"""Always extend from this if you're making a plugin. That way, a deletion
of an article will CASCADE to your plugin, and the database will be kept
clean. Furthermore, it's possible to list all plugins and maintain generic
properties in the future..."""
article = models.ForeignKey(Article, on_delete=models.CASCADE,
verbose_name=_(u"article"))
created = models.DateTimeField(auto_now_add=True, verbose_name=_(u"created"))
modified = models.DateTimeField(auto_now=True, verbose_name=_(u"created"))
deleted = models.BooleanField(default=False)
def purge(self):
"""Remove related contents completely, ie. media files."""
pass
class Meta:
abstract = True
class ReusablePlugin(ArticlePlugin):
app_label = settings.APP_LABEL
class ReusablePlugin(ArticlePlugin):
"""Extend from this model if you have a plugin that may be related to many
articles. Please note that the ArticlePlugin.article ForeignKey STAYS! This
is in order to maintain an explicit set of permissions. If you do not like this,
you can override can_read and can_write."""
# The article on which the plugin was originally created.
# Used to apply permissions.
ArticlePlugin.article.on_delete=models.SET_NULL
ArticlePlugin.article.verbose_name=_(u'original article')
ArticlePlugin.article.help_text=_(u'Permissions are inherited from this article')
ArticlePlugin.article.on_delete=models.SET_NULL
ArticlePlugin.article.null = True
ArticlePlugin.article.blank = True
......@@ -54,9 +65,6 @@ class ReusablePlugin(ArticlePlugin):
return self.article.can_write(*args, **kwargs)
return False
class Meta:
abstract = True
def save(self, *args, **kwargs):
# Automatically make the original article the first one in the added set
......@@ -67,24 +75,30 @@ class ReusablePlugin(ArticlePlugin):
super(ReusablePlugin, self).save(*args, **kwargs)
class Meta:
app_label = settings.APP_LABEL
class RevisionPluginCreateError(Exception): pass
class RevisionPlugin(models.Model):
class RevisionPlugin(ArticlePlugin):
"""
Inherit from this model and make sure to specify an article when
saving a new instance. This way, a new revision will be created, and
users are able to roll back to the a previous revision (containing some
other instance of your plugin).
users are able to roll back to the a previous revision (in which your
plugin wasn't related to the article).
Furthermore, your plugin relation is kept when new revisions are created.
Usage:
class YourPlugin(RevisionPlugin):
...
Creating new plugins:
Creating new plugins instances:
YourPlugin(article=article_instance, ...) or
YourPlugin.objects.create(article=article_instance, ...)
"""
revision = models.ForeignKey(ArticleRevision, on_delete=models.CASCADE)
......@@ -109,5 +123,22 @@ class RevisionPlugin(models.Model):
self.revision = new_revision
class Meta:
abstract = True
app_label = settings.APP_LABEL
######################################################
# SIGNAL HANDLERS
######################################################
# Look at me. I'm a plane.
# And the plane becomes a metaphor for my life.
# It's my art, when I disguise my body in the shape of a plane.
# (Shellac, 1993)
def update_revision_plugins(instance, *args, **kwargs):
"""Every time a new article revision is created, we update all active
plugins to match this article revision"""
if kwargs.get('created', False):
p_revisions = RevisionPlugin.objects.filter(article=instance.article, deleted=False)
p_revisions.update(revision=instance)
signals.post_save.connect(update_revision_plugins, ArticleRevision)
\ No newline at end of file
......@@ -56,7 +56,7 @@ class URLPath(MPTTModel):
super(URLPath, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
assert not self.parent and self.get_children(), "You cannot delete a root article with children."
assert not (self.parent and self.get_children()), "You cannot delete a root article with children."
super(URLPath, self).delete(*args, **kwargs)
class Meta:
......
......@@ -24,12 +24,15 @@ class AttachmentPreprocessor(markdown.preprocessors.Preprocessor):
m = ATTACHMENT_RE.match(line)
if m:
attachment_id = m.group('id').strip()
attachment = models.Attachment.objects.get(article=self.markdown.article,
try:
attachment = models.Attachment.objects.get(article=self.markdown.article,
id=attachment_id)
url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id,
'attachment_id':attachment.id,})
line = line.replace(m.group(1), u"""<span class="attachment"><a href="%s" title="%s">%s</a>""" %
(url, _(u"Click to download file"), attachment.original_filename))
url = reverse('wiki:attachments_download', kwargs={'article_id': self.markdown.article.id,
'attachment_id':attachment.id,})
line = line.replace(m.group(1), u"""<span class="attachment"><a href="%s" title="%s">%s</a>""" %
(url, _(u"Click to download file"), attachment.original_filename))
except models.Attachment.DoesNotExist:
line = line.replace(m.group(1), u"""<span class="attachment attachment-deleted">Attachment with ID #%s is deleted.</span>""" % attachment_id)
new_text.append(line)
return new_text
......@@ -6,7 +6,7 @@ import settings
from wiki import managers
from wiki.conf import settings as wiki_settings
from wiki.models.pluginbase import ReusablePlugin
from wiki.models.article import BaseRevision
from wiki.models.article import BaseRevisionMixin
class IllegalFileExtension(Exception):
"""File extension on upload is not allowed"""
......@@ -63,7 +63,7 @@ def upload_path(instance, filename):
return path.join(upload_path, filename + '.upload')
class AttachmentRevision(BaseRevision):
class AttachmentRevision(BaseRevisionMixin, models.Model):
attachment = models.ForeignKey('Attachment')
......@@ -75,6 +75,7 @@ class AttachmentRevision(BaseRevision):
class Meta:
verbose_name = _(u'attachment revision')
verbose_name_plural = _(u'attachment revisions')
ordering = ('created',)
get_latest_by = ('revision_number',)
app_label = wiki_settings.APP_LABEL
......
{% extends "wiki/base.html" %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Delete" %} "{{ attachment.current_revision.get_filename }}"{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% block wiki_contents_tab %}
<div class="tabbable tabs-top" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "attachments" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
{% if attachment.article == article %}
<h2>{% trans "Delete" %} "{{ attachment.current_revision.get_filename }}"?</h2>
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
The file may be referenced on other articles. Deleting it means that they will loose their references to this file. The following articles reference this file:
{% endblocktrans %}
</p>
<ul>
{% for a in attachment.articles.all %}
<li style="font-size: 150%;">{{ a.current_revision.title }}</li>
{% endfor %}
</ul>
<hr />
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-danger btn-large">
<span class="icon-upload"></span>
{% trans "Delete it!" %}
</button>
</div>
</form>
{% else %}
<h2>{% trans "Remove" %} "{{ attachment.current_revision.get_filename }}"?</h2>
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
You can remove a reference to a file, but it will retain its references on other articles.
{% endblocktrans %}
</p>
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-danger btn-large">
<span class="icon-upload"></span>
{% trans "Remove reference" %}
</button>
</div>
</form>
{% endif %}
</div>
{% if attachment.article == article %}
<h2>{% trans "Delete" %} "{{ attachment.current_revision.get_filename }}"?</h2>
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
The file may be referenced on other articles. Deleting it means that they will loose their references to this file. The following articles reference this file:
{% endblocktrans %}
</p>
<ul>
{% for a in attachment.articles.all %}
<li style="font-size: 150%;">{{ a.current_revision.title }}</li>
{% endfor %}
</ul>
<hr />
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-danger btn-large">
<span class="icon-upload"></span>
{% trans "Delete it!" %}
</button>
</div>
</form>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
{% else %}
<h2>{% trans "Remove" %} "{{ attachment.current_revision.get_filename }}"?</h2>
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
You can remove a reference to a file, but it will retain its references on other articles.
{% endblocktrans %}
</p>
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-danger btn-large">
<span class="icon-upload"></span>
{% trans "Remove reference" %}
</button>
</div>
</form>
{% endif %}
{% endblock %}
{% extends "wiki/base.html" %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "History of" %} "{{ attachment.current_revision.get_filename }}"{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
<div class="tabbable tabs-top" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "attachments" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<h2>{% trans "History of" %} "{{ attachment.current_revision.get_filename }}"</h2>
<table class="table table-striped table-bordered">
<tr>
<th>{% trans "Date" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Description" %}</th>
<th>{% trans "File" %}</th>
<th>{% trans "Size" %}</th>
<th style="text-align: right">{% trans "Action" %}</th>
</tr>
{% for revision in revisions %}
<tr>
<td>
{{ revision.created }}
{% if revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td>
<td>
{% 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 %}
</td>
<td>{{ revision.description|default:_("<em>No description</em>")|safe }}</td>
<td>{{ revision.get_filename }}</td>
<td>{{ revision.file.size|filesizeformat }}</td>
<td style="text-align: right">
<form method="POST" action="{% url 'wiki:attachments_revision_change' path=urlpath.path article_id=article.id attachment_id=attachment.id revision_id=revision.id %}">
{% csrf_token %}
<a href="{% url 'wiki:attachments_download' path=urlpath.path article_id=article.id attachment_id=attachment.id revision_id=revision.id %}" class="btn btn-primary">
<span class="icon-download"></span>
{% trans "Download" %}
</a>
{% if revision.attachment.article|can_write:user %}
<button{% if revision == attachment.current_revision %} disabled="disabled"{% endif %} class="btn">
<span class="icon-flag"></span>
{% trans "Use this!" %}
</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</table>
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}"><span class="icon-arrow-left"></span> {% trans "Go back" %}</a>
</div>
</div>
{% block wiki_contents_tab %}
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
<h2>{% trans "History of" %} "{{ attachment.current_revision.get_filename }}"</h2>
<table class="table table-striped table-bordered">
<tr>
<th>{% trans "Date" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Description" %}</th>
<th>{% trans "File" %}</th>
<th>{% trans "Size" %}</th>
<th style="text-align: right">{% trans "Action" %}</th>
</tr>
{% for revision in revisions %}
<tr>
<td>
{{ revision.created }}
{% if revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td>
<td>
{% 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 %}
</td>
<td>{{ revision.description|default:_("<em>No description</em>")|safe }}</td>
<td>{{ revision.get_filename }}</td>
<td>{{ revision.file.size|filesizeformat }}</td>
<td style="text-align: right">
<form method="POST" action="{% url 'wiki:attachments_revision_change' path=urlpath.path article_id=article.id attachment_id=attachment.id revision_id=revision.id %}">
{% csrf_token %}
<a href="{% url 'wiki:attachments_download' path=urlpath.path article_id=article.id attachment_id=attachment.id revision_id=revision.id %}" class="btn btn-primary">
<span class="icon-download"></span>
{% trans "Download" %}
</a>
{% if revision.attachment.article|can_write:user %}
<button{% if revision == attachment.current_revision %} disabled="disabled"{% endif %} class="btn">
<span class="icon-flag"></span>
{% trans "Use this!" %}
</button>
{% endif %}
</form>
</td>
</tr>
{% endfor %}
</table>
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}"><span class="icon-arrow-left"></span> {% trans "Go back" %}</a>
{% endblock %}
{% extends "wiki/base.html" %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Replace" %} "{{ attachment.current_revision.get_filename }}"{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents_tab %}
{% block wiki_contents %}
<div class="tabbable tabs-top" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "attachments" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<h2>{% trans "Replace" %} "{{ attachment.current_revision.get_filename }}"</h2>
{% if attachment.articles.count > 1 %}
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
Replacing an attachment means adding a new file that will be used in its place. All references to the file will be replaced by the one you upload and the file will be downloaded as <strong>{{ filename }}</strong>. Please note that this attachment is in use on other articles, you may distort contents. However, do not hestitate to take advantage of this and make replacements for the listed articles where necessary. This way of working is more efficient....
{% endblocktrans %}
</p>
<h3>{% trans "Articles using" %} {{ attachment.current_revision.get_filename }}</h3>
<ul>
{% for a in attachment.articles.all %}<li>{{ a.current_revision.title }}</li>{% endfor %}
</ul>
<hr />
{% else %}
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
Replacing an attachment means adding a new file that will be used in its place. All references to the file will be replaced by the one you upload and the file will be downloaded as <strong>{{ filename }}</strong>.
{% endblocktrans %}
</p>
{% endif %}
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-primary">
<span class="icon-upload"></span>
{% trans "Upload replacement" %}
</button>
</div>
</form>
</div>
</div>
<h2>{% trans "Replace" %} "{{ attachment.current_revision.get_filename }}"</h2>
{% if attachment.articles.count > 1 %}
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
Replacing an attachment means adding a new file that will be used in its place. All references to the file will be replaced by the one you upload and the file will be downloaded as <strong>{{ filename }}</strong>. Please note that this attachment is in use on other articles, you may distort contents. However, do not hestitate to take advantage of this and make replacements for the listed articles where necessary. This way of working is more efficient....
{% endblocktrans %}
</p>
<h3>{% trans "Articles using" %} {{ attachment.current_revision.get_filename }}</h3>
<ul>
{% for a in attachment.articles.all %}<li>{{ a.current_revision.title }}</li>{% endfor %}
</ul>
<hr />
{% else %}
<p class="lead">
{% blocktrans with attachment.original_filename as filename %}
Replacing an attachment means adding a new file that will be used in its place. All references to the file will be replaced by the one you upload and the file will be downloaded as <strong>{{ filename }}</strong>.
{% endblocktrans %}
</p>
{% endif %}
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
<form method="POST" class="form-horizontal" id="attachment_form" enctype="multipart/form-data">
{% wiki_form form %}
<div class="form-actions">
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}" class="btn">
<span class="icon-arrow-left"></span>
{% trans "Go back" %}
</a>
<button class="btn btn-primary">
<span class="icon-upload"></span>
{% trans "Upload replacement" %}
</button>
</div>
</form>
{% endblock %}
{% extends "wiki/base.html" %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n humanize %}
{% load url from future %}
{% block pagetitle %}{% trans "Add file to" %} "{{ article.current_revision.title }}"{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents_tab %}
{% block wiki_contents %}
<h2>{% trans "Add attachment from other article" %}</h2>
<div class="tabbable tabs-top" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "attachments" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<h2>{% trans "Add existing attachment to" %} {{ article.current_revision.title }}</h2>
<form method="GET" action="{% url 'wiki:attachments_search' path=urlpath.path article_id=article.id %}" class="form-search">
{{ search_form.query }}
<button class="btn">
<span class="icon-search"></span>
{% trans "Search files and articles" %}
</button>
</form>
<form method="GET" action="{% url 'wiki:attachments_search' path=urlpath.path article_id=article.id %}" class="form-search">
{{ search_form.query }}
<button class="btn">
<span class="icon-search"></span>
{% trans "Search files and articles" %}
{% if attachments %}
<table class="table table-striped table-bordered">
<tr>
<th>{% trans "File" %}</th>
<th>{% trans "Main article" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
<th style="text-align: right">{% trans "Action" %}</th>
</tr>
{% for attachment in attachments %}
<tr>
<td>
<h4>{{ attachment.original_filename }}</h4>
{{ attachment.current_revision.description|default:_("<em>No description</em>")|safe }}
</td>
<td>
<strong>{{ attachment.article.current_revision.title }}</strong>
</td>
<td>
{{ attachment.current_revision.created }}
{% if attachment.current_revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td>
<td>
{% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
</td>
<td>{{ attachment.current_revision.file.size|filesizeformat }}</td>
<td style="text-align: right">
<form method="POST" action="{% url 'wiki:attachments_add' path=urlpath.path article_id=article.id attachment_id=attachment.id %}">
{% csrf_token %}
<a href="{% url 'wiki:attachments_download' path=urlpath.path article_id=article.id attachment_id=attachment.id %}" class="btn">
<span class="icon-download"></span>
{% trans "Download" %}
</a>
<button class="btn btn-primary">
<span class="icon-plus"></span>
{% trans "Add to article" %}
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p><em>{% trans "Your search did not return any results" %}</em></p>
{% endif %}
{% if attachments %}
<table class="table table-striped table-bordered">
<tr>
<th>{% trans "File" %}</th>
<th>{% trans "Main article" %}</th>
<th>{% trans "Date" %}</th>
<th>{% trans "Uploaded by" %}</th>
<th>{% trans "Size" %}</th>
<th style="text-align: right">{% trans "Action" %}</th>
</tr>
{% for attachment in attachments %}
<tr>
<td>
<h4>{{ attachment.original_filename }}</h4>
{{ attachment.current_revision.description|default:_("<em>No description</em>")|safe }}
</td>
<td>
<strong>{{ attachment.article.current_revision.title }}</strong>
</td>
<td>
{{ attachment.current_revision.created }}
{% if attachment.current_revision.deleted %}<span class="badge badge-important">{% trans "deleted" %}</span>{% endif %}
</td>
<td>
{% if attachment.current_revision.user %}{{ attachment.current_revision.user }}{% else %}{% if user|is_moderator %}{{ attachment.current_revision.ip_address|default:"anonymous (IP not logged)" }}{% else %}{% trans "anonymous (IP logged)" %}{% endif %}{% endif %}
</td>
<td>{{ attachment.current_revision.file.size|filesizeformat }}</td>
<td style="text-align: right">
<form method="POST" action="{% url 'wiki:attachments_add' path=urlpath.path article_id=article.id attachment_id=attachment.id %}">
{% csrf_token %}
<a href="{% url 'wiki:attachments_download' path=urlpath.path article_id=article.id attachment_id=attachment.id %}" class="btn">
<span class="icon-download"></span>
{% trans "Download" %}
</a>
<button class="btn btn-primary">
<span class="icon-plus"></span>
{% trans "Add to article" %}
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p><em>{% trans "Your search did not return any results" %}</em></p>
{% endif %}
{% with query as appended_value and "query" as appended_key %}
{% include "wiki/includes/pagination.html" %}
{% endwith %}
<p>
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}"><span class="icon-arrow-left"></span> {% trans "Go back" %}</a>
</p>
</div>
</div>
{% with query as appended_value and "query" as appended_key %}
{% include "wiki/includes/pagination.html" %}
{% endwith %}
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
{% endblock %}
<p>
<a href="{% url 'wiki:attachments_index' path=urlpath.path article_id=article.id %}"><span class="icon-arrow-left"></span> {% trans "Go back" %}</a>
</p>
{% endblock %}
......@@ -32,7 +32,7 @@ class AttachmentView(ArticleMixin, FormView):
return super(AttachmentView, self).dispatch(request, article, *args, **kwargs)
# WARNING! The below decorator silences other exceptions that may occur!
@transaction.commit_manually
#@transaction.commit_manually
def form_valid(self, form):
try:
attachment_revision = form.save(commit=False)
......@@ -48,18 +48,17 @@ class AttachmentView(ArticleMixin, FormView):
except models.IllegalFileExtension, e:
transaction.rollback()
messages.error(self.request, _(u'Your file could not be saved: %s') % e)
except Exception:
transaction.rollback()
messages.error(self.request, _(u'Your file could not be saved, probably because of a permission error on the web server.'))
#except Exception:
# transaction.rollback()
# messages.error(self.request, _(u'Your file could not be saved, probably because of a permission error on the web server.'))
transaction.commit()
if self.urlpath:
return redirect("wiki:attachments_index", self.urlpath.path)
# TODO: What if no urlpath?
#transaction.commit()
return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
def get_context_data(self, **kwargs):
kwargs['attachments'] = self.attachments
kwargs['search_form'] = forms.SearchForm()
kwargs['selected_tab'] = 'attachments'
return super(AttachmentView, self).get_context_data(**kwargs)
......@@ -78,6 +77,7 @@ class AttachmentHistoryView(ArticleMixin, TemplateView):
def get_context_data(self, **kwargs):
kwargs['attachment'] = self.attachment
kwargs['revisions'] = self.attachment.attachmentrevision_set.all().order_by('-revision_number')
kwargs['selected_tab'] = 'attachments'
return super(AttachmentHistoryView, self).get_context_data(**kwargs)
......@@ -102,10 +102,7 @@ class AttachmentReplaceView(ArticleMixin, FormView):
self.attachment.save()
messages.success(self.request, _(u'%s uploaded and replaces old attachment.') % attachment_revision.get_filename())
if self.urlpath:
return redirect("wiki:attachments_index", self.urlpath.path)
# TODO: What if we do not have a urlpath?
return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
def get_form(self, form_class):
form = FormView.get_form(self, form_class)
......@@ -117,6 +114,7 @@ class AttachmentReplaceView(ArticleMixin, FormView):
def get_context_data(self, **kwargs):
kwargs['attachment'] = self.attachment
kwargs['selected_tab'] = 'attachments'
return super(AttachmentReplaceView, self).get_context_data(**kwargs)
class AttachmentDownloadView(ArticleMixin, View):
......@@ -132,7 +130,6 @@ class AttachmentDownloadView(ArticleMixin, View):
return super(AttachmentDownloadView, self).dispatch(request, article, *args, **kwargs)
def get(self, request, *args, **kwargs):
if self.revision:
return send_file(request, self.revision.file.path,
self.revision.created, self.attachment.original_filename)
......@@ -157,12 +154,11 @@ class AttachmentChangeRevisionView(ArticleMixin, View):
self.attachment.save()
messages.success(self.request, _(u'Current revision changed for %s.') % self.attachment.original_filename)
if self.urlpath:
return redirect("wiki:attachments_index", path=self.urlpath.path)
# TODO: What if this hasn't got a urlpath.
else:
pass
return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
def get_context_data(self, **kwargs):
kwargs['selected_tab'] = 'attachments'
return ArticleMixin.get_context_data(self, **kwargs)
class AttachmentAddView(ArticleMixin, View):
......@@ -177,12 +173,8 @@ class AttachmentAddView(ArticleMixin, View):
messages.success(self.request, _(u'Added a reference to "%(att)s" from "%(art)s".') %
{'att': self.attachment.original_filename,
'art': self.article.current_revision.title})
if self.urlpath:
return redirect("wiki:attachments_index", path=self.urlpath.path)
# TODO: What if this hasn't got a urlpath.
else:
pass
return redirect("wiki:attachments_index", path=self.urlpath.path, article_id=self.article.id)
class AttachmentDeleteView(ArticleMixin, FormView):
......@@ -214,12 +206,11 @@ class AttachmentDeleteView(ArticleMixin, FormView):
self.attachment.articles.remove(self.article)
messages.info(self.request, _(u'This article is no longer related to the file %s.') % self.attachment.original_filename)
if self.urlpath:
return redirect("wiki:get_url", path=self.urlpath.path)
# TODO: No urlpath?
return redirect("wiki:get", path=self.urlpath.path, article_id=self.article.id)
def get_context_data(self, **kwargs):
kwargs['attachment'] = self.attachment
kwargs['selected_tab'] = 'attachments'
return super(AttachmentDeleteView, self).get_context_data(**kwargs)
......@@ -254,4 +245,5 @@ class AttachmentSearchView(ArticleMixin, ListView):
kwargs['query'] = self.query
kwargs.update(kwargs_article)
kwargs.update(kwargs_listview)
kwargs['selected_tab'] = 'attachments'
return kwargs
......@@ -19,12 +19,13 @@ class ArticleSubscription(wiki_models.pluginbase.ArticlePlugin, Subscription):
'article': self.article.current_revision.title,
'type': self.notification_type.label})
def default_url(article):
def default_url(article, urlpath=None):
try:
urlpath = wiki_models.URLPath.objects.get(articles=article)
url = reverse('wiki:get_url', args=(urlpath.path,))
if not urlpath:
urlpath = wiki_models.URLPath.objects.get(articles=article)
url = reverse('wiki:get', kwargs={'path': urlpath.path})
except wiki_models.URLPath.DoesNotExist:
url = None
url = reverse('wiki:get', kwargs={'article_id': article.id})
return url
def post_article_save(instance, **kwargs):
......
notify_latest_id = 0;
notify_update_timeout = 30000;
notify_update_timeout_adjust = 1.2; // factor to adjust between each timeout.
function notify_update() {
jsonWrapper(URL_NOTIFY_GET_NEW, function (data) {
......@@ -29,9 +31,15 @@ function notify_mark_read() {
});
}
function update_timeout() {
setTimeout("notify_update()", notify_update_timeout);
setTimeout("update_timeout()", notify_update_timeout);
notify_update_timeout *= notify_update_timeout_adjust;
}
$(document).ready(function () {
notify_update();
// Update every second minute.
setInterval("notify_update()", 120000);
// Don't check immediately... some users just click through pages very quickly.
setTimeout("notify_update()", 2000);
update_timeout();
})
......@@ -15,23 +15,16 @@
<li style="float: left">
<h1 style="margin-top: -10px;">{{ article.current_revision.title }}</h1>
</li>
{% with "view" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
{% include "wiki/includes/article_menu.html" %}
</ul>
<div class="tab-content">
{% block wiki_contents_tab %}
{% wiki_render article %}
{% endblock %}
</div>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "view" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
</div>
<p style="margin-top: 20px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></p>
{% endblock %}
......@@ -11,7 +11,7 @@
<form method="POST" class="form-horizontal">
{% wiki_form create_form %}
<div class="form-actions">
<a href="{% url 'wiki:get_url' parent_urlpath.path %}" class="btn btn-large">
<a href="{% url 'wiki:get' path=parent_urlpath.path %}" class="btn btn-large">
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %}
</a>
......
{% extends "wiki/base.html" %}
{% load wiki_tags i18n sekizai_tags %}
{% load url from future %}
{% block pagetitle %}{% trans "Delete article" %}{% endblock %}
{% block wiki_contents %}
{% include "wiki/includes/editormedia.html" %}
<h1 class="page-header">{% trans "Delete" %} "{{ article.current_revision.title }}"?</h1>
<form method="POST" class="form-horizontal">
{% wiki_form create_form %}
<div class="form-actions">
<a href="{% url 'wiki:get' path=parent_urlpath.path %}" class="btn btn-large">
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %}
</a>
<button type="submit" name="save_changes" class="btn btn-primary btn-large">
<span class="icon-plus"></span>
{% trans "Create article" %}
</button>
</div>
</form>
{% endblock %}
{% extends "wiki/base.html" %}
{% extends "wiki/article.html" %}
{% load wiki_tags i18n %}
{% load url from future %}
{% block pagetitle %}{% trans "Edit" %}: {{ article.current_revision.title }}{% endblock %}
{% block wiki_breadcrumbs %}
{% include "wiki/includes/breadcrumbs.html" %}
{% endblock %}
{% block wiki_contents %}
{% block wiki_contents_tab %}
<div class="tabbable tabs-top" style="margin-top: 20px;">
<ul class="nav nav-tabs">
{% with "edit" as selected %}
{% include "wiki/includes/article_menu.html" %}
{% endwith %}
<li>
<h1 style="margin-top: -10px;">
{{ article.current_revision.title }}
</h1>
</li>
</ul>
<div class="tab-content">
<form method="POST" class="form-horizontal">
{% include "wiki/includes/editor.html" %}
<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_url' urlpath.path %}'">
<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_url' urlpath.path %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
<a class="pull-right btn btn-danger">
<span class="icon-trash"></span>
{% trans "Delete article" %}
</a>
</div>
<div class="modal hide fade" id="previewModal" style="width: 80%; min-height: 500px; margin-left: -40%;">
<div class="modal-body">
<iframe name="previewWindow" style="width: 100%; min-height: 400px; border: 0;" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
<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_url' urlpath.path %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</div>
</form>
</div>
<form method="POST" class="form-horizontal">
{% include "wiki/includes/editor.html" %}
<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 %}'">
<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 %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
<a href="{% url 'wiki:delete' path=urlpath.path article_id=article.id %}" class="pull-right btn btn-danger">
<span class="icon-trash"></span>
{% trans "Delete article" %}
</a>
</div>
<div class="modal hide fade" id="previewModal" style="width: 80%; min-height: 500px; margin-left: -40%;">
<div class="modal-body">
<iframe name="previewWindow" style="width: 100%; min-height: 400px; border: 0;" frameborder="0"></iframe>
</div>
<div class="tabbable tabs-below" style="margin-top: 20px;">
<ul class="nav nav-tabs">
<li style="margin-top: 10px;"><em>{% trans "Article last modified:" %} {{ article.current_revision.modified }}</em></li>
</ul>
<div class="modal-footer">
<a href="#" class="btn btn-large" data-dismiss="modal">
<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 %}'">
<span class="icon-ok"></span>
{% trans "Save changes" %}
</button>
</div>
</div>
</form>
{% endblock %}
{% load i18n wiki_tags %}{% load url from future %}
{% with selected_tab as selected %}
{% for plugin in plugins %}
{% if plugin.article_tab %}
<li class="pull-right{% if selected == plugin.slug %} active{% endif %}">
{% if urlpath %}
<a href="{% url 'wiki:plugin_url' path=urlpath.path slug=plugin.slug %}">
<span class="{{ plugin.article_tab.1 }}"></span>
{{ plugin.article_tab.0 }}
</a>
{% else %}
<a href="{% url 'wiki:plugin' slug=plugin.slug article_id=article.id %}">
<a href="{% url 'wiki:plugin' slug=plugin.slug article_id=article.id path=urlpath.path %}">
<span class="{{ plugin.article_tab.1 }}"></span>
{{ plugin.article_tab.0 }}
</a>
{% endif %}
</li>
{% endif %}
{% endfor %}
{% if urlpath %}
<li class="pull-right{% if selected == "settings" %} active{% endif %}">
{% if not user.is_anonymous %}
<a href="{% url 'wiki:settings_url' path=urlpath.path %}">
<a href="{% url 'wiki:settings' article_id=article.id path=urlpath.path %}">
<span class="icon-wrench"></span>
{% trans "Settings" %}
</a>
{% endif %}
</li>
<li class="pull-right{% if selected == "history" %} active{% endif %}">
<a href="{% url 'wiki:history_url' urlpath.path %}">
<a href="{% url 'wiki:history' article_id=article.id path=urlpath.path %}">
<span class="icon-time"></span>
{% trans "Changes" %}
</a>
</li>
<li class="pull-right{% if selected == "edit" %} active{% endif %}">
<a href="{% url 'wiki:edit_url' urlpath.path %}">
<a href="{% url 'wiki:edit' article_id=article.id path=urlpath.path %}">
<span class="icon-edit"></span>
{% trans "Edit" %}
</a>
</li>
<li class="pull-right{% if selected == "view" %} active{% endif %}">
<a href="{% url 'wiki:get_url' urlpath.path %}">
<a href="{% url 'wiki:get' article_id=article.id path=urlpath.path %}">
<span class="icon-home"></span>
{% trans "View" %}
</a>
</li>
{% else %}
<li class="pull-right{% if selected == "settings" %} active{% endif %}">
{% if not user.is_anonymous %}
<a href="{% url 'wiki:settings' article_id=article.id %}">
<span class="icon-wrench"></span>
{% trans "Settings" %}
</a>
{% endif %}
</li>
<li class="pull-right{% if selected == "history" %} active{% endif %}">
<a href="{% url 'wiki:history' article_id=article.id %}">
<span class="icon-time"></span>
{% trans "Changes" %}
</a>
</li>
<li class="pull-right{% if selected == "edit" %} active{% endif %}">
<a href="{% url 'wiki:edit' article_id=article.id %}">
<span class="icon-edit"></span>
{% trans "Edit" %}
</a>
</li>
<li class="pull-right{% if selected == "view" %} active{% endif %}">
<a href="{% url 'wiki:get' article_id=article.id %}">
<span class="icon-home"></span>
{% trans "View" %}
</a>
</li>
{% endif %}
{% endwith %}
......@@ -3,10 +3,10 @@
<ul class="breadcrumb pull-left" class="">
{% for ancestor in urlpath.get_ancestors.all %}
<span class="divider">/</span>
<li><a href="{% url 'wiki:get_url' ancestor.path %}">{{ ancestor.article.current_revision.title }}</a></li>
<li><a href="{% url 'wiki:get' path=ancestor.path %}">{{ ancestor.article.current_revision.title }}</a></li>
{% endfor %}
<span class="divider">/</span>
<li class="active"><a href="{% url 'wiki:get_url' urlpath.path %}">{{ article.current_revision.title }}</a></li>
<li class="active"><a href="{% url 'wiki:get' path=urlpath.path %}">{{ article.current_revision.title }}</a></li>
<span class="divider">/</span>
</ul>
<div class="pull-left" style="margin-left: 10px;">
......@@ -18,7 +18,7 @@
<ul class="dropdown-menu">
{% for child in urlpath.get_children %}
<li>
<a href="{% url 'wiki:get_url' child.path %}">
<a href="{% url 'wiki:get' path=child.path %}">
{{ child.article.current_revision.title }}
</a>
</li>
......@@ -33,7 +33,7 @@
</div>
</div>
<div class="pull-left" style="margin-left: 10px;">
<a class="btn" href="{% url 'wiki:create_url' urlpath.path %}" style="padding: 7px;">
<a class="btn" href="{% url 'wiki:create' path=urlpath.path %}" style="padding: 7px;">
<span class="icon-plus"></span>
{% trans "Add article" %}
</a>
......
......@@ -8,7 +8,7 @@ from wiki.core import plugins_registry
urlpatterns = patterns('',
url('^$', article.ArticleView.as_view(), name='root', kwargs={'path': ''}),
url('^create-root/$', 'wiki.views.article.root_create', name='root_create'),
url('^_revision/diff/(\d+)/$', 'wiki.views.article.diff', name='diff'),
url('^_revision/diff/(?P<revision_id>\d+)/$', 'wiki.views.article.diff', name='diff'),
)
if settings.ACCOUNT_HANDLING:
......@@ -21,12 +21,12 @@ if settings.ACCOUNT_HANDLING:
urlpatterns += patterns('',
# This one doesn't work because it don't know where to redirect after...
url('^_revision/change/(?P<article_id>\d+)/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
url('^_revision/preview/(?P<article_id>\d+)/$', 'wiki.views.article.preview', name='preview_revision'),
url('^_revision/merge/(?P<article_id>\d+)/(?P<revision_id>\d+)/preview/$', 'wiki.views.article.merge', name='merge_revision_preview', kwargs={'preview': True}),
# Paths decided by article_ids
url('^(?P<article_id>\d+)/$', article.ArticleView.as_view(), name='get'),
url('^(?P<article_id>\d+)/delete/$', article.Delete.as_view(), name='delete'),
url('^(?P<article_id>\d+)/edit/$', article.Edit.as_view(), name='edit'),
url('^(?P<article_id>\d+)/preview/$', 'wiki.views.article.preview', name='preview'),
url('^(?P<article_id>\d+)/history/$', article.History.as_view(), name='history'),
......@@ -42,32 +42,28 @@ for plugin in plugins_registry._cache.values():
plugin_urlpatterns = getattr(plugin, 'urlpatterns', None)
if slug and plugin_urlpatterns:
urlpatterns += patterns('',
url('^(?P<path>.+/|)_plugin/'+slug+'/', include(plugin_urlpatterns)),
url('^(?P<article_id>\d+)(?P<path>)/plugin/'+slug+'/', include(plugin_urlpatterns)),
url('^(?P<article_id>\d+)/plugin/'+slug+'/', include(plugin_urlpatterns)),
url('^(?P<path>.+/|)_plugin/'+slug+'/', include(plugin_urlpatterns)),
)
urlpatterns += patterns('',
# Paths decided by URLs
url('^(?P<path>.+/|)_create/$', article.Create.as_view(), name='create_url'),
url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit_url'),
url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview_url'),
url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history_url'),
url('^(?P<path>.+/|)_settings/$', article.Settings.as_view(), name='settings_url'),
url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision_url'),
url('^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$', 'wiki.views.article.merge', name='merge_revision_url'),
url('^(?P<path>.+/|)_plugin/(?P<slug>\w+)/$', article.Plugin.as_view(), name='plugin_url'),
)
urlpatterns += patterns('',
url('^(?P<path>.+/|)$', article.ArticleView.as_view(), name='get_url'),
url('^(?P<path>.+/|)_create/$', article.Create.as_view(), name='create'),
url('^(?P<path>.+/|)_delete/$', article.Edit.as_view(), name='delete'),
url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit'),
url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview'),
url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history'),
url('^(?P<path>.+/|)_settings/$', article.Settings.as_view(), name='settings'),
url('^(?P<path>.+/|)_revision/change/(?P<revision_id>\d+)/$', 'wiki.views.article.change_revision', name='change_revision'),
url('^(?P<path>.+/|)_revision/merge/(?P<revision_id>\d+)/$', 'wiki.views.article.merge', name='merge_revision'),
url('^(?P<path>.+/|)_plugin/(?P<slug>\w+)/$', article.Plugin.as_view(), name='plugin'),
url('^(?P<path>.+/|)$', article.ArticleView.as_view(), name='get'),
)
def get_pattern(app_name="wiki", namespace="wiki"):
"""Every url resolution takes place as "wiki:view_name".
You should not attempt to have multiple deployments of the wiki on
one site.
You should not attempt to have multiple deployments of the wiki in a
single Django project.
https://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces
"""
return urlpatterns, app_name, namespace
\ No newline at end of file
......@@ -28,8 +28,8 @@ class Logout(View):
def get(self, request, *args, **kwargs):
auth_logout(request)
messages.info(request, _(u"You are no longer logged in. Bye bye!"))
print redirect("wiki:get_url", URLPath.root().path)
return redirect("wiki:get_url", URLPath.root().path)
print redirect("wiki:get", URLPath.root().path)
return redirect("wiki:get", URLPath.root().path)
class Login(FormView):
......@@ -47,5 +47,5 @@ class Login(FormView):
messages.info(self.request, _(u"You are now logged in! Have fun!"))
if self.request.GET.get("next", None):
return redirect(self.request.GET['next'])
return redirect("wiki:get_url", URLPath.root().path)
return redirect("wiki:get", URLPath.root().path)
......@@ -18,15 +18,19 @@ from wiki.core.diff import simple_merge
from wiki.decorators import get_article, json_view
from django.core.urlresolvers import reverse
from django.db import transaction
from wiki.core.exceptions import NoRootURL
class ArticleView(ArticleMixin, TemplateView, ):
class ArticleView(ArticleMixin, TemplateView):
template_name="wiki/article.html"
@method_decorator(get_article(can_read=True))
def dispatch(self, request, article, *args, **kwargs):
return super(ArticleView, self).dispatch(request, article, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs['selected_tab'] = 'view'
return ArticleMixin.get_context_data(self, **kwargs)
class Create(FormView, ArticleMixin):
......@@ -78,14 +82,14 @@ class Create(FormView, ArticleMixin):
else:
messages.error(self.request, _(u"There was an error creating this article."))
transaction.commit()
return redirect('wiki:get_url', '')
return redirect('wiki:get', '')
url = self.get_success_url()
transaction.commit()
return url
def get_success_url(self):
return redirect('wiki:get_url', self.newpath.path)
return redirect('wiki:get', self.newpath.path)
def get_context_data(self, **kwargs):
kwargs['parent_urlpath'] = self.urlpath
......@@ -95,6 +99,56 @@ class Create(FormView, ArticleMixin):
return super(Create, self).get_context_data(**kwargs)
class Delete(FormView, ArticleMixin):
form_class = forms.DeleteForm
template_name="wiki/delete.html"
@method_decorator(get_article(can_write=True))
def dispatch(self, request, article, *args, **kwargs):
super_return = super(Delete, self).dispatch(request, article, *args, **kwargs)
# Where to go after deletion...
self.next = request.GET.get('next', None)
if not self.next:
if self.urlpath:
self.next = reverse('wiki:get', path=self.urlpath.parent.path)
else:
for art_obj in self.objectforarticle_set.filter(is_mptt=True):
self.next = reverse('wiki:get', kwargs={'article_id': art_obj.parent.article.id})
return super_return
def get_initial(self):
return {'revision': self.article.current_revision}
def get_form(self, form_class):
form = FormView.get_form(self, form_class)
if self.request.user.has_perm('wiki.moderator'):
form.fields['purge'].widget = forms.forms.CheckboxInput()
def form_valid(self, form):
cd = form.cleaned_data
if self.request.user.has_perm('wiki.moderator') and cd['purge']:
pass
messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!'))
else:
revision = models.ArticleRevision()
revision.inherit_predecessor(self.article)
revision.deleted = True
self.article.add_revision(revision)
messages.success(self.request, _(u'This article is now marked as deleted! Thanks for keeping the site free from unwanted material!'))
def get_success_url(self):
return redirect(self.next)
def get_context_data(self, **kwargs):
kwargs['parent_urlpath'] = self.urlpath
kwargs['parent_article'] = self.article
kwargs['create_form'] = kwargs.pop('form', None)
kwargs['editor'] = editors.editor
return super(Create, self).get_context_data(**kwargs)
class Edit(FormView, ArticleMixin):
form_class = forms.EditForm
......@@ -122,16 +176,15 @@ class Edit(FormView, ArticleMixin):
return self.get_success_url()
def get_success_url(self):
if not self.urlpath is None:
return redirect("wiki:get_url", self.urlpath.path)
# TODO: Where to go if it's a different object? It's probably
# an ajax callback, so we don't care... but should perhaps return
# a status
return
if self.urlpath:
return redirect("wiki:get", path=self.urlpath.path)
return redirect('wiki:get', article_id=self.article.id)
def get_context_data(self, **kwargs):
kwargs['edit_form'] = kwargs.pop('form', None)
kwargs['editor'] = editors.editor
kwargs['selected_tab'] = 'edit'
return super(Edit, self).get_context_data(**kwargs)
......@@ -151,6 +204,7 @@ class History(ListView, ArticleMixin):
kwargs_listview = ListView.get_context_data(self, **kwargs)
kwargs.update(kwargs_article)
kwargs.update(kwargs_listview)
kwargs['selected_tab'] = 'history'
return kwargs
@method_decorator(get_article(can_read=True))
......@@ -198,7 +252,9 @@ class Settings(ArticleMixin, TemplateView):
usermessage = form.get_usermessage()
if usermessage:
messages.success(self.request, usermessage)
return redirect('wiki:settings_url', self.urlpath.path)
if self.urlpath:
return redirect('wiki:settings', path=self.urlpath.path)
return redirect('wiki:settings', article_id=self.article.id)
else:
form = Form(self.article, self.request.user)
self.forms.append(form)
......@@ -211,9 +267,12 @@ class Settings(ArticleMixin, TemplateView):
return super(Settings, self).get(*args, **kwargs)
def get_success_url(self):
return redirect('wiki:settings_url', self.urlpath.path)
if self.urlpath:
return redirect('wiki:settings', path=self.urlpath.path)
return redirect('wiki:settings', article_id=self.article.id)
def get_context_data(self, **kwargs):
kwargs['selected_tab'] = 'settings'
kwargs['forms'] = self.forms
return super(Settings, self).get_context_data(**kwargs)
......@@ -226,10 +285,9 @@ def change_revision(request, article, revision_id=None, urlpath=None):
article.save()
messages.success(request, _(u'The article %s is now set to display revision #%d') % (revision.title, revision.revision_number))
if urlpath:
return redirect("wiki:history_url", urlpath.path)
return redirect("wiki:history", path=urlpath.path)
else:
# TODO: Where to go if not a urlpath object?
pass
return redirect('wiki:history', article_id=article.id)
# TODO: Throw in a class-based view
@get_article(can_read=True)
......@@ -306,7 +364,9 @@ def merge(request, article, revision_id, urlpath=None, template_file="wiki/previ
{'r1': revision.revision_number,
'r2': old_revision.revision_number})
if urlpath:
return redirect('wiki:edit_url', urlpath.path)
return redirect('wiki:edit', path=urlpath.path)
else:
return redirect('wiki:edit', article_id=article.id)
c = RequestContext(request, {'article': article,
......@@ -319,6 +379,14 @@ def merge(request, article, revision_id, urlpath=None, template_file="wiki/previ
return render_to_response(template_file, c)
def root_create(request):
try:
root = models.URLPath.root()
if not root.article:
root.delete()
raise NoRootURL
return redirect('wiki:get', path=root.path)
except NoRootURL:
pass
if not request.user.has_perm('wiki.add_article'):
return redirect(reverse("wiki:login") + "?next=" + reverse("wiki:root_create"))
if request.method == 'POST':
......
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