Commit 2eae064d by benjaoming

Correcting errors caused by removal of Article.title field. Almost done with Delete view.

parent c3303e15
...@@ -51,12 +51,19 @@ def get_article(func=None, can_read=True, can_write=False): ...@@ -51,12 +51,19 @@ def get_article(func=None, can_read=True, can_write=False):
pathlist = filter(lambda x: x!="", path.split("/"),) pathlist = filter(lambda x: x!="", path.split("/"),)
path = "/".join(pathlist[:-1]) path = "/".join(pathlist[:-1])
parent = models.URLPath.get_by_path(path) parent = models.URLPath.get_by_path(path)
return redirect(reverse("wiki:create_url", args=(parent.path,)) + "?slug=%s" % pathlist[-1]) return redirect(reverse("wiki:create", kwargs={'path': parent.path,}) + "?slug=%s" % pathlist[-1])
except models.URLPath.DoesNotExist: except models.URLPath.DoesNotExist:
# TODO: Make a nice page # TODO: Make a nice page
return HttpResponseNotFound("This article was not found, and neither was the parent. This page should look nicer.") return HttpResponseNotFound("This article was not found, and neither was the parent. This page should look nicer.")
# TODO: If the article is not found but it exists, there is a permission error! # TODO: If the article is not found but it exists, there is a permission error!
article = get_object_or_404(articles, id=urlpath.article.id) if urlpath.article:
article = get_object_or_404(articles, id=urlpath.article.id)
else:
# Somehow article is gone
return_url = reverse('wiki:get', kwargs={'path': urlpath.parent.path})
urlpath.delete()
return return_url
kwargs['urlpath'] = urlpath kwargs['urlpath'] = urlpath
......
...@@ -13,7 +13,7 @@ from wiki.editors import editor ...@@ -13,7 +13,7 @@ from wiki.editors import editor
from wiki.core.diff import simple_merge from wiki.core.diff import simple_merge
from django.forms.widgets import HiddenInput from django.forms.widgets import HiddenInput
class CreateRoot(forms.Form): class CreateRootForm(forms.Form):
title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.')) title = forms.CharField(label=_(u'Title'), help_text=_(u'Initial title of the article. May be overridden with revision titles.'))
content = forms.CharField(label=_(u'Type in some contents'), content = forms.CharField(label=_(u'Type in some contents'),
...@@ -175,7 +175,7 @@ class CreateForm(forms.Form): ...@@ -175,7 +175,7 @@ class CreateForm(forms.Form):
self.urlpath_parent = urlpath_parent self.urlpath_parent = urlpath_parent
title = forms.CharField(label=_(u'Title'),) title = forms.CharField(label=_(u'Title'),)
slug = forms.SlugField(label=_(u'Slug'), help_text=_(u"This will be the address where your article can be found. Use only alphanumeric characters and '-' or '_'."),) slug = forms.SlugField(label=_(u'Slug'), help_text=_(u"This will be the address where your article can be found. Use only alphanumeric characters and - or _. Note that you cannot change the slug after creating the article."),)
content = forms.CharField(label=_(u'Contents'), content = forms.CharField(label=_(u'Contents'),
required=False, widget=editor.get_widget()) #@UndefinedVariable required=False, widget=editor.get_widget()) #@UndefinedVariable
...@@ -186,8 +186,14 @@ class CreateForm(forms.Form): ...@@ -186,8 +186,14 @@ class CreateForm(forms.Form):
slug = self.cleaned_data['slug'] slug = self.cleaned_data['slug']
if slug[0] == "_": if slug[0] == "_":
raise forms.ValidationError(_(u'A slug may not begin with an underscore.')) raise forms.ValidationError(_(u'A slug may not begin with an underscore.'))
if models.URLPath.objects.filter(slug=slug, parent=self.urlpath_parent): already_existing_slug = models.URLPath.objects.filter(slug=slug, parent=self.urlpath_parent)
raise forms.ValidationError(_(u'A slug named "%s" already exists.') % slug) if already_existing_slug:
slug = already_existing_slug[0]
if slug.deleted:
raise forms.ValidationError(_(u'A deleted article with slug "%s" already exists.') % slug)
else:
raise forms.ValidationError(_(u'A slug named "%s" already exists.') % slug)
return slug return slug
class PermissionsForm(forms.ModelForm): class PermissionsForm(forms.ModelForm):
...@@ -224,10 +230,23 @@ class PermissionsForm(forms.ModelForm): ...@@ -224,10 +230,23 @@ class PermissionsForm(forms.ModelForm):
class DeleteForm(forms.Form): class DeleteForm(forms.Form):
confirm = forms.BooleanField(required=False) def __init__(self, *args, **kwargs):
purge = forms.BooleanField(widget=HiddenInput(), required=False) self.article = kwargs.pop('article')
self.children = kwargs.pop('children')
super(DeleteForm, self).__init__(*args, **kwargs)
confirm = forms.BooleanField(required=False,
label=_(u'Confirm'))
purge = forms.BooleanField(widget=HiddenInput(), required=False,
label=_(u'Purge'),
help_text=_(u'Purge the article: Completely remove it (and all its contents) with no undo.'))
revision = forms.ModelChoiceField(models.ArticleRevision.objects.all(),
widget=HiddenInput(), required=False)
def clean(self): def clean(self):
cd = self.cleaned_data cd = self.cleaned_data
if not cd['confirm']: if not cd['confirm']:
raise forms.ValidationError(_(u'You are not sure enough!')) raise forms.ValidationError(_(u'You are not sure enough!'))
if cd['revision'] != self.article.current_revision:
raise forms.ValidationError(_(u'While you tried to delete this article, it was modified. TAKE CARE!'))
return cd
\ No newline at end of file
...@@ -110,7 +110,7 @@ class URLPath(MPTTModel): ...@@ -110,7 +110,7 @@ class URLPath(MPTTModel):
if not root_nodes: if not root_nodes:
# (get_or_create does not work for MPTT models??) # (get_or_create does not work for MPTT models??)
root = cls.objects.create(site=site) root = cls.objects.create(site=site)
article = Article(title=title) article = Article()
article.add_revision(ArticleRevision(title=title, **kwargs), article.add_revision(ArticleRevision(title=title, **kwargs),
save=True) save=True)
article.add_object_relation(root) article.add_object_relation(root)
...@@ -122,7 +122,7 @@ class URLPath(MPTTModel): ...@@ -122,7 +122,7 @@ class URLPath(MPTTModel):
def create_article(cls, parent, slug, site=None, title="Root", **kwargs): def create_article(cls, parent, slug, site=None, title="Root", **kwargs):
if not site: site = Site.objects.get_current() if not site: site = Site.objects.get_current()
newpath = cls.objects.create(site=site, parent=parent, slug=slug) newpath = cls.objects.create(site=site, parent=parent, slug=slug)
article = Article(title=title) article = Article()
article.add_revision(ArticleRevision(title=title, **kwargs), article.add_revision(ArticleRevision(title=title, **kwargs),
save=True) save=True)
article.add_object_relation(newpath) article.add_object_relation(newpath)
...@@ -153,16 +153,16 @@ def on_article_delete(instance, *args, **kwargs): ...@@ -153,16 +153,16 @@ def on_article_delete(instance, *args, **kwargs):
lost_and_found = URLPath.objects.create(slug=settings.LOST_AND_FOUND_SLUG, lost_and_found = URLPath.objects.create(slug=settings.LOST_AND_FOUND_SLUG,
parent=URLPath.root(), parent=URLPath.root(),
site=site,) site=site,)
article = Article(title=_(u"Lost and found"), article = Article(group_read = True,
group_read = True,
group_write = False, group_write = False,
other_read = False, other_read = False,
other_write = False) other_write = False)
article.add_revision(ArticleRevision( article.add_revision(ArticleRevision(
content=_(u'Articles who lost their parents' content=_(u'Articles who lost their parents'
'==============================='))) '==============================='),
title=_(u"Lost and found")))
for urlpath in URLPath.objects.filter(articles__article=article, site=site): for urlpath in URLPath.objects.filter(articles__article=instance, site=site):
for child in urlpath.get_children(): for child in urlpath.get_children():
child.move_to(lost_and_found) child.move_to(lost_and_found)
......
# Key for django_notify
ARTICLE_EDIT = "article_edit" ARTICLE_EDIT = "article_edit"
ARTICLE_CREATE = "article_create"
...@@ -6,7 +6,7 @@ from django.db.models import signals ...@@ -6,7 +6,7 @@ from django.db.models import signals
from django_notify import notify from django_notify import notify
from django_notify.models import Subscription from django_notify.models import Subscription
from wiki.plugins.notifications import ARTICLE_CREATE, ARTICLE_EDIT from wiki.plugins.notifications import ARTICLE_EDIT
from wiki import models as wiki_models from wiki import models as wiki_models
from wiki.core import plugins_registry from wiki.core import plugins_registry
...@@ -28,23 +28,19 @@ def default_url(article, urlpath=None): ...@@ -28,23 +28,19 @@ def default_url(article, urlpath=None):
url = reverse('wiki:get', kwargs={'article_id': article.id}) url = reverse('wiki:get', kwargs={'article_id': article.id})
return url return url
def post_article_save(instance, **kwargs):
if kwargs.get('created', False):
url = default_url(instance)
notify(_(u'New article created: %s') % instance.title, ARTICLE_CREATE,
target_object=instance, url=url)
def post_article_revision_save(instance, **kwargs): def post_article_revision_save(instance, **kwargs):
if kwargs.get('created', False): if kwargs.get('created', False):
url = default_url(instance.article) url = default_url(instance.article)
notify(_(u'Article modified: %s') % instance.title, ARTICLE_EDIT, if instance.deleted:
target_object=instance.article, url=url) notify(_(u'Article deleted: %s') % instance.title, ARTICLE_EDIT,
target_object=instance.article, url=url)
# Create notifications when new articles are saved. We do NOT care elif instance.previous_revision:
# about Article objects that are just modified, because many properties notify(_(u'Article modified: %s') % instance.title, ARTICLE_EDIT,
# are modified without any notifications necessary! target_object=instance.article, url=url)
signals.post_save.connect(post_article_save, sender=wiki_models.Article,) else:
notify(_(u'New article created: %s') % instance.title, ARTICLE_EDIT,
target_object=instance, url=url)
# Whenever a new revision is created, we notifý users that an article # Whenever a new revision is created, we notifý users that an article
# was edited # was edited
signals.post_save.connect(post_article_revision_save, sender=wiki_models.ArticleRevision,) signals.post_save.connect(post_article_revision_save, sender=wiki_models.ArticleRevision,)
......
...@@ -5,22 +5,54 @@ ...@@ -5,22 +5,54 @@
{% block pagetitle %}{% trans "Delete article" %}{% endblock %} {% block pagetitle %}{% trans "Delete article" %}{% endblock %}
{% block wiki_contents %} {% block wiki_contents %}
{% include "wiki/includes/editormedia.html" %} <h1 class="page-header">{% trans "Delete" %} "{{ article.current_revision.title }}"</h1>
<h1 class="page-header">{% trans "Delete" %} "{{ article.current_revision.title }}"?</h1>
<form method="POST" class="form-horizontal"> {% if cannot_delete_root %}
{% wiki_form create_form %} <p class="lead">{% trans "You cannot delete a root article." %}</p>
<div class="form-actions"> <p><a href="{% url 'wiki:get' path=urlpath.path article_id=article.id %}">{% trans "Go back" %}</a></p>
<a href="{% url 'wiki:get' path=parent_urlpath.path %}" class="btn btn-large"> {% else %}
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %} {% if cannot_delete_children %}
</a>
<button type="submit" name="save_changes" class="btn btn-primary btn-large"> <p class="alert alert-error"><strong>{% trans "You cannot delete this article because you do not have permission to delete articles with children. Try to remove the children manually one-by-one." %}</strong></p>
<span class="icon-plus"></span>
{% trans "Create article" %} {% endif %}
</button>
</div> {% if children %}
</form>
<p class="lead">{% trans "You are deleting an article. This means that its children will loose their connection to the rest of the tree and moved to Lost and Found. Only do this if you know what you're doing." %}</p>
<h2>{% trans "Articles that will be orphans" %}</h2>
<ul>
{% for child in children %}
<li><a href="{% url 'wiki:get' path=child.article.path %}" target="_blank">{{ child.article }}</a></li>
{% endfor %}
</ul>
{% else %}
<p class="lead">{% trans "You are deleting an article. Please confirm." %}</p>
{% endif %}
{% if not cannot_delete_children %}
<form method="POST" class="form-horizontal">
{% wiki_form delete_form %}
<div class="form-actions">
<a href="{% url 'wiki:get' path=urlpath.path article_id=article.id %}" class="btn btn-large">
<span class="icon-circle-arrow-left"></span>
{% trans "Go back" %}
</a>
<button type="submit" name="save_changes" class="btn btn-critical btn-large">
<span class="icon-plus"></span>
{% trans "Delete article" %}
</button>
</div>
</form>
{% endif %}
{% endif %}
{% endblock %} {% endblock %}
...@@ -49,7 +49,7 @@ for plugin in plugins_registry._cache.values(): ...@@ -49,7 +49,7 @@ for plugin in plugins_registry._cache.values():
urlpatterns += patterns('', urlpatterns += patterns('',
# Paths decided by URLs # Paths decided by URLs
url('^(?P<path>.+/|)_create/$', article.Create.as_view(), name='create'), url('^(?P<path>.+/|)_create/$', article.Create.as_view(), name='create'),
url('^(?P<path>.+/|)_delete/$', article.Edit.as_view(), name='delete'), url('^(?P<path>.+/|)_delete/$', article.Delete.as_view(), name='delete'),
url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit'), url('^(?P<path>.+/|)_edit/$', article.Edit.as_view(), name='edit'),
url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview'), url('^(?P<path>.+/|)_preview/$', 'wiki.views.article.preview', name='preview'),
url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history'), url('^(?P<path>.+/|)_history/$', article.History.as_view(), name='history'),
......
...@@ -19,6 +19,7 @@ from wiki.decorators import get_article, json_view ...@@ -19,6 +19,7 @@ from wiki.decorators import get_article, json_view
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.db import transaction from django.db import transaction
from wiki.core.exceptions import NoRootURL from wiki.core.exceptions import NoRootURL
from django_notify.decorators import disable_notify
class ArticleView(ArticleMixin, TemplateView): class ArticleView(ArticleMixin, TemplateView):
...@@ -106,47 +107,95 @@ class Delete(FormView, ArticleMixin): ...@@ -106,47 +107,95 @@ class Delete(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):
super_return = super(Delete, self).dispatch(request, article, *args, **kwargs) urlpath = kwargs.get('urlpath', None)
# Where to go after deletion... # Where to go after deletion...
self.next = request.GET.get('next', None) self.next = request.GET.get('next', None)
self.cannot_delete_root = False
if not self.next: if not self.next:
if self.urlpath: if urlpath and urlpath.parent:
self.next = reverse('wiki:get', path=self.urlpath.parent.path) self.next = reverse('wiki:get', kwargs={'path': urlpath.parent.path})
elif urlpath:
self.cannot_delete_root = True
else: else:
for art_obj in self.objectforarticle_set.filter(is_mptt=True): for art_obj in article.articleforobject_set.filter(is_mptt=True):
self.next = reverse('wiki:get', kwargs={'article_id': art_obj.parent.article.id}) if art_obj.content_object.parent:
return super_return self.next = reverse('wiki:get', kwargs={'article_id': art_obj.content_object.parent.article.id})
else:
self.cannot_delete_root = True
# Fetch children if necessary
self.children = []
if not self.cannot_delete_root:
self.children = article.get_children()
self.cannot_delete_children = False
if self.children and not request.user.has_perm('wiki.moderator'):
self.cannot_delete_children = True
return super(Delete, self).dispatch(request, article, *args, **kwargs)
def get_initial(self): def get_initial(self):
return {'revision': self.article.current_revision} return {'revision': self.article.current_revision}
def get_form(self, form_class): def get_form(self, form_class):
form = FormView.get_form(self, form_class) form = super(Delete, self).get_form(form_class)
if self.request.user.has_perm('wiki.moderator'): if self.request.user.has_perm('wiki.moderator'):
form.fields['purge'].widget = forms.forms.CheckboxInput() form.fields['purge'].widget = forms.forms.CheckboxInput()
return form
def get_form_kwargs(self):
kwargs = FormView.get_form_kwargs(self)
kwargs['article'] = self.article
kwargs['children'] = self.children
return kwargs
@disable_notify
def delete_children(self, purge=False):
if purge:
for child in self.children:
child.delete()
else:
for child in self.children:
revision = models.ArticleRevision()
revision.inherit_predecessor(child)
revision.deleted = True
child.add_revision(revision)
def form_valid(self, form): def form_valid(self, form):
cd = form.cleaned_data cd = form.cleaned_data
if self.cannot_delete_root or self.cannot_delete_children:
messages.error(self.request, _(u'This article cannot be deleted because it has children or is a root article.'))
return redirect('wiki:get', article_id=self.article.id)
# First, remove children
self.delete_children(purge=cd['purge'])
if self.request.user.has_perm('wiki.moderator') and cd['purge']: if self.request.user.has_perm('wiki.moderator') and cd['purge']:
pass self.article.delete()
messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!')) messages.success(self.request, _(u'This article together with all its contents are now completely gone! Thanks!'))
else: else:
revision = models.ArticleRevision() revision = models.ArticleRevision()
revision.inherit_predecessor(self.article) revision.inherit_predecessor(self.article)
revision.deleted = True revision.deleted = True
self.article.add_revision(revision) 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!')) 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): def get_success_url(self):
return redirect(self.next) return redirect(self.next)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
kwargs['parent_urlpath'] = self.urlpath kwargs['delete_form'] = kwargs.pop('form', None)
kwargs['parent_article'] = self.article kwargs['cannot_delete_root'] = self.cannot_delete_root
kwargs['create_form'] = kwargs.pop('form', None) children = []
kwargs['editor'] = editors.editor cnt = 0
return super(Create, self).get_context_data(**kwargs) for child in self.children:
if cnt > 21: break
children.append(child)
kwargs['children'] = children[:20]
kwargs['children_more'] = len(children) > 20
kwargs['cannot_delete_children'] = self.cannot_delete_children
return super(Delete, self).get_context_data(**kwargs)
class Edit(FormView, ArticleMixin): class Edit(FormView, ArticleMixin):
...@@ -378,6 +427,7 @@ def merge(request, article, revision_id, urlpath=None, template_file="wiki/previ ...@@ -378,6 +427,7 @@ def merge(request, article, revision_id, urlpath=None, template_file="wiki/previ
'content': content}) 'content': content})
return render_to_response(template_file, c) return render_to_response(template_file, c)
# TODO: Should be a class-based view
def root_create(request): def root_create(request):
try: try:
root = models.URLPath.root() root = models.URLPath.root()
...@@ -390,13 +440,13 @@ def root_create(request): ...@@ -390,13 +440,13 @@ def root_create(request):
if not request.user.has_perm('wiki.add_article'): if not request.user.has_perm('wiki.add_article'):
return redirect(reverse("wiki:login") + "?next=" + reverse("wiki:root_create")) return redirect(reverse("wiki:login") + "?next=" + reverse("wiki:root_create"))
if request.method == 'POST': if request.method == 'POST':
create_form = forms.CreateRoot(request.POST) create_form = forms.CreateRootForm(request.POST)
if create_form.is_valid(): if create_form.is_valid():
models.URLPath.create_root(title=create_form.cleaned_data["title"], models.URLPath.create_root(title=create_form.cleaned_data["title"],
content=create_form.cleaned_data["content"]) content=create_form.cleaned_data["content"])
return redirect("wiki:root") return redirect("wiki:root")
else: else:
create_form = forms.CreateRoot() create_form = forms.CreateRootForm()
c = RequestContext(request, {'create_form': create_form, c = RequestContext(request, {'create_form': create_form,
'editor': editors.editor,}) 'editor': editors.editor,})
......
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