Merge lp:~widelands-dev/widelands-website/bug-1399461_error_on_renaming_article into lp:widelands-website
- bug-1399461_error_on_renaming_article
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 447 |
Proposed branch: | lp:~widelands-dev/widelands-website/bug-1399461_error_on_renaming_article |
Merge into: | lp:widelands-website |
Diff against target: |
402 lines (+188/-44) 10 files modified
templates/admin/wiki/article/change_form.html (+9/-0) templates/wiki/backlinks.html (+38/-0) templates/wiki/edit.html (+4/-4) templates/wiki/view.html (+9/-0) wiki/admin.py (+5/-5) wiki/forms.py (+24/-8) wiki/migrations/0002_auto_20161218_1056.py (+19/-0) wiki/models.py (+2/-2) wiki/urls.py (+3/-0) wiki/views.py (+75/-25) |
To merge this branch: | bzr merge lp:~widelands-dev/widelands-website/bug-1399461_error_on_renaming_article |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
SirVer | code | Approve | |
Review via email: mp+314033@code.launchpad.net |
Commit message
Prevent double names of wiki articles. Add a 'What links here' page.
Description of the change
This fixes bug 1399461 'Renaming an article triggers an error, if article exists' by making the name of an article a unique value on database level. Also prevents reverting an article if the reverting implies renaming and the name to which the article wants to be renamed exists in the meantime. An example (which shows also some other difficulties), imagine some month/years left between the steps:
1. Article "A_1" was created
2. Article "B_1" was created
3. "A_1" get renamed to "A" -> All old links called "A_1" redirects then to article "A"
4. "B_1" get renamed to "A_1" -> All old links called "A_1" open now article "A_1" (not "A" anymore)
5. "A" wants to be reverted to a revision where its name was "A_1" -> This is now prevented, because "A_1" already exists (got renamed from "B_1")
I wanted to prevent step 4 (renaming an article to a name where a redirect exists) but i couldn't fiddle this out. In general i think redirects are not really a good thing at all, because it may lead into many redirects after some time. So i implemented a 'backlinks' page ('What links here'). This page shows following information:
1. All articles which have links to the current name of this article
2. All articles which have links which points to (one of) the old name(s) of this article (redirects)
3. If this article is not linked at all, show a message with a request to link it somewhere
The 2. point has also a request to change the links in all listed articles to point to the current name. Generally speaking: Please remove the redirects
Point 3. has a disadvantage: If an article is only linked over the main menu, it shows also the message to link this article somewhere. Not the best, but i have no idea how to fix this in a convenient manner.
Changes to admin page of wiki:
1. Add a link to show all changesets of this article
If this branch get deployed, one has to run './manage.py migrate' to apply the changes.
SirVer (sirver) wrote : | # |
kaputtnik (franku) wrote : | # |
> > In general i think redirects are not really a good thing at all, because it
> may lead into many redirects after some time.
>
> While I agree here, they are also sorta required: if some external site links
> to a site in our Wiki, we want this link to stay valid forever ideally. So we
> have to track renames (or completely forbid them) and forward to the new page.
Forbid renaming isn't a good thing also. If one wants to put the content of an article under a new name, he has to create then a new article and copy and paste the content from the old article to the new one. So all changesets are gone and reverting to the previous situation is not possible anymore.
What should we do with following situation? An existing example:
1. https:/
2. https:/
2. is a redirect to 1. But it is currently possible to create a new article GameHelp:
https:/
When saving this new article, all old redirects are gone, respectively all Links to GameHelp open then the new article. Should creating of articles prevented if a redirect exists? On the long run this would mean the available amount of article names shrinks, because some articles reserve two (or more) names.
> About the (internal) redirects: Is it not in our power to fix all internal
> links that linked to the old page into links that link to the new page? I
> mean, we can just make a new revision for each page algorithmically that just
> does the renames. No user interaction is needed.
If we want redirects to stay forever, this isn't needed, isn't it? And reverting to a previous revision put then the old links in again. But we may need such a functionality also for bug 1595294 "Get rid of wikiwordification".
This is all a bit complicated to me... because our redirects are some kind of "soft-redirects": To determine a redirect the code looks just for the requested name in all changesets. If it is found the corresponding article get opened and the String "(redirected from ...)" is shown. There is no real 'redirect-object' and i have trouble to abstract the redirects. I think other wikis handle redirects in another way: If a renaming is done a new article with the old name is created and the content is the redirect with a special syntax. So there are two existing articles (the article it self, and the redirect to it), whereas we have only one.
SirVer (sirver) wrote : | # |
> Should creating of articles prevented if a redirect exists?
Yes, I think this is acceptable. We create maybe 10 articles per year, it will be a while till we run out of names, even including redirects.
> If we want redirects to stay forever, this isn't needed, isn't it?
While that is true, it might still be nice to fix all references we have internally to have consistency.
> There is no real 'redirect-object' and i have trouble to abstract the redirects.
One solution would be to have a separate table that holds only the existing redirects. That table is queried first whenever a article is requested and if a match is found, the redirect is evaluated.
kaputtnik (franku) wrote : | # |
> > Should creating of articles prevented if a redirect exists?
>
> Yes, I think this is acceptable. We create maybe 10 articles per year, it will
> be a while till we run out of names, even including redirects.
Ok, then i try to prevent it in this branch :-)
>
> > If we want redirects to stay forever, this isn't needed, isn't it?
>
> While that is true, it might still be nice to fix all references we have
> internally to have consistency.
I think this would be better to manage if we have the other bug fixed and we have also a consistent syntax for internal wiki links.
> > There is no real 'redirect-object' and i have trouble to abstract the
> redirects.
>
> One solution would be to have a separate table that holds only the existing
> redirects. That table is queried first whenever a article is requested and if
> a match is found, the redirect is evaluated.
I have to think about it. But i guess this would be also better to do it in another branch, because this one is only to prevent the renaming to a name an existing article, or a redirect, has.
kaputtnik (franku) wrote : | # |
Puhh... hopefully i get it now. Giving the example of
1. https:/
2. https:/
- Trying to open /wiki/edit/GameHelp opens now 'Description' for editing.
Trying to change the title is handled as follows:
For existing articles those circumstances are prevented and a message is shown in the form:
- If the title is in a changeset (so redirects and old links will not break)
- If the title already exists as normal article (handled in wiki/forms.py)
For new articles:
- If one opens /wiki/edit/foo and immediately wants to rename it to 'bar' without saving first, the previous checks are also done. There is one difference: If 'bar' exists as normal article, django checks this on database level, a message is then also shown in the form.
I think this is ready but want to make a last test in the next days. A question:
I am unsure about a nested 'try: ... except:' clause. Is this good coding practice? See:
SirVer (sirver) wrote : | # |
Solution sounds good to me, but I did not test it.
> I am unsure about a nested 'try: ... except:' clause. Is this good coding practice? See:
Yes, that seems fine. I would not know how to code this in another fashion.
SirVer (sirver) : | # |
kaputtnik (franku) wrote : | # |
Thanks for the review :-)
Let's wait for Gun if she complains against my english pronouncing :-)
kaputtnik (franku) wrote : | # |
Found a failure... tomorrow i want to merge this on the alpha site, so anybody can take a look at it and test.
kaputtnik (franku) wrote : | # |
Merged but not committed on alpha, migrated the changes related to the database over there. For testing one has to start the alpha site.
SirVer (sirver) wrote : | # |
I am unsure about the last comment. Is that a request for testing help? If so, I'd suggest you lanuch and leave the alpha site running.
kaputtnik (franku) wrote : | # |
I have interpreted the statement:
> ... , but did not test it
as you would like to test. I think on the alpha site this is easier. The alpha site is running now.
Maybe i have one thing of the changes to explain further: To show the Changesets for an Article one has to open an article in the admin page and on the top right there is now a link called "Changesets".
Once you told me the alpha site should not run for a longer time, so i shut it down every time i had finished my tests. May we could clarify this on IRC?
GunChleoc (gunchleoc) wrote : | # |
Linguistic proofreading done.
kaputtnik (franku) wrote : | # |
Thanks a lot :-)
kaputtnik (franku) wrote : | # |
This is merged and deployed now on the productive website.
Alpha is offline again.
SirVer (sirver) wrote : | # |
Sorry for holding this off :(. I wanted to test this indeed, but I did not get around to it.
> Once you told me the alpha site should not run for a longer time, so i shut it down every time i had finished my tests. May we could clarify this on IRC?
As long as necessary, as short as possible. If it is online for a significant percentage of the year, it will be found by search engines leading to confusing (and mostly insignificant) links in search results. Search is our main influx of users, so this would be suboptimal. A few days at a time if there is something to test is probably not hurting us though. That was my entire thought process.
kaputtnik (franku) wrote : | # |
> Sorry for holding this off :(
Sorry that i am such impatient in this case... but i think this branch does not hurt if it is productive. There are no writes to Database or to the storage, so the worst thing that could happen is a not catched server error (we had for a long time nor such errors :-D ). I just wanted to close this thing and work on stuff where this branch would be a help for.
Thanks for the clarification related to alpha :-)
I have seen that you are ill and hopefully you get well soon...
Preview Diff
1 | === added directory 'templates/admin' |
2 | === added directory 'templates/admin/wiki' |
3 | === added directory 'templates/admin/wiki/article' |
4 | === added file 'templates/admin/wiki/article/change_form.html' |
5 | --- templates/admin/wiki/article/change_form.html 1970-01-01 00:00:00 +0000 |
6 | +++ templates/admin/wiki/article/change_form.html 2017-01-18 20:19:31 +0000 |
7 | @@ -0,0 +1,9 @@ |
8 | +{% extends "admin/change_form.html" %} |
9 | +{% load i18n admin_urls %} |
10 | + |
11 | +{% block object-tools-items %} |
12 | + <li> |
13 | + <a href="/admin/wiki/changeset/?article__title={{ original.title }}" class="historylink">{% trans "Changesets" %}</a> |
14 | + </li> |
15 | + {% if has_absolute_url %}<li><a href="{{ absolute_url }}" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif %} |
16 | +{% endblock %} |
17 | |
18 | === added file 'templates/wiki/backlinks.html' |
19 | --- templates/wiki/backlinks.html 1970-01-01 00:00:00 +0000 |
20 | +++ templates/wiki/backlinks.html 2017-01-18 20:19:31 +0000 |
21 | @@ -0,0 +1,38 @@ |
22 | +{% extends 'wiki/base.html' %} |
23 | +{% load wiki_extras %} |
24 | +{% load i18n %} |
25 | +{% block title %} |
26 | +{{ name }} - Backlinks - {{block.super}} |
27 | +{% endblock %} |
28 | + |
29 | +{% block content %} |
30 | +<div class="posRight small"> |
31 | + <a class="invertedColor" href="{% url 'wiki_article' name %}">Back to article</a> |
32 | +</div> |
33 | +<h1>{% trans "Backlinks of " %} {{ name }}</h1> |
34 | +<div class="blogEntry"> |
35 | + {% if found_links or found_old_links %} |
36 | + {% if found_links %} |
37 | + <h3>The following pages have link(s) which point to this article:</h3> |
38 | + <ul> |
39 | + {% for article in found_links %} |
40 | + <li><a href="{% url 'wiki_article' article.title %}">{{ article.title }}</a></li> |
41 | + {% endfor %} |
42 | + </ul> |
43 | + {% endif %} |
44 | + {% if found_old_links %} |
45 | + <h3>The following articles have links which point to one of the old name(s) of this article (redirects):</h3> |
46 | + <ul> |
47 | + {% for article in found_old_links %} |
48 | + <li><a href="{% url 'wiki_article' article.title %}">{{ article.title }}</a> |
49 | + (has at least one link to: "{{ article.old_title }}")</li> |
50 | + {% endfor %} |
51 | + </ul> |
52 | + {% endif %} |
53 | + {% else %} |
54 | + <p><span class="errormessage">Every Wikipage must be linked from at least one another page.</span> |
55 | + Please link it <img src="/wlmedia/img/smileys/face-smile.png" alt="face-smile.png"> See <a href="/wiki/WikiSyntax/#links">Wiki Syntax</a> for help.</p></p> |
56 | + {% endif %} |
57 | +</div> |
58 | +{% endblock %} |
59 | + |
60 | |
61 | === modified file 'templates/wiki/edit.html' |
62 | --- templates/wiki/edit.html 2016-11-26 20:55:21 +0000 |
63 | +++ templates/wiki/edit.html 2017-01-18 20:19:31 +0000 |
64 | @@ -60,17 +60,17 @@ |
65 | <!-- Title --> |
66 | <tr> |
67 | <th><label for="id_title">{% trans "Title" %}:</label></th> |
68 | - <td>{{ form.title.errors }}{{ form.title }}</td> |
69 | + <td class="errormessage">{{ form.title.errors }}{{ form.title }}</td> |
70 | </tr> |
71 | <!-- Content --> |
72 | <tr><th colspan="2"><label for="id_content">{% trans "Content" %}:</label></th></tr> |
73 | - <tr><td colspan="2" >{{ form.content.errors }}{{ form.content }}</td></tr> |
74 | + <tr><td colspan="2" class="errormessage">{{ form.content.errors }}{{ form.content }}</td></tr> |
75 | <!-- Summary --> |
76 | <tr><th colspan="2"><label for="id_summary">{% trans "Page Summary" %}:</label></th></tr> |
77 | - <tr><td colspan="2" >{{ form.summary.errors }}{{ form.summary }}</td></tr> |
78 | + <tr><td colspan="2" class="errormessage">{{ form.summary.errors }}{{ form.summary }}</td></tr> |
79 | <!-- Comment --> |
80 | <tr><th colspan=2><label for="id_comment">{% trans "Comment for this revision" %}:</label></th></tr> |
81 | - <tr><td colspan=2 >{{ form.comment.errors }}{{ form.comment }}</td></tr> |
82 | + <tr><td colspan=2 class="errormessage">{{ form.comment.errors }}{{ form.comment }}</td></tr> |
83 | </table> |
84 | <!-- Markup as hidden element --> |
85 | <input type="hidden" name="markup" value="mrk" /> |
86 | |
87 | === modified file 'templates/wiki/view.html' |
88 | --- templates/wiki/view.html 2016-04-26 16:10:04 +0000 |
89 | +++ templates/wiki/view.html 2017-01-18 20:19:31 +0000 |
90 | @@ -18,6 +18,8 @@ |
91 | <a class="invertedColor" href="{% url 'wiki_edit' article.title %}">{% trans "Edit this article" %}</a> |
92 | | |
93 | <a class="invertedColor" href="{% url 'wiki_article_history' article.title %}">{% trans "Editing history" %}</a> |
94 | + | |
95 | + <a class="invertedColor" href="{% url 'backlinks' article.title %}">{% trans "Backlinks" %}</a> |
96 | {% if can_observe %} |
97 | | |
98 | {% if is_observing %} |
99 | @@ -29,6 +31,13 @@ |
100 | {% endif %} |
101 | </div> |
102 | <h1>{{ article.title }}</h1> |
103 | + |
104 | + {% if messages %} |
105 | + {% for message in messages %} |
106 | + <p class="errormessage">{{ message }}</p> |
107 | + {% endfor %} |
108 | + {% endif %} |
109 | + |
110 | <div class="blogEntry"> |
111 | {% if not article.id %} |
112 | <p> |
113 | |
114 | === modified file 'wiki/admin.py' |
115 | --- wiki/admin.py 2016-11-22 22:17:23 +0000 |
116 | +++ wiki/admin.py 2017-01-18 20:19:31 +0000 |
117 | @@ -15,7 +15,7 @@ |
118 | |
119 | class ArticleAdmin(admin.ModelAdmin): |
120 | search_fields = ['title'] |
121 | - list_display = ('title', 'markup', 'created_at', 'last_update',) |
122 | + list_display = ('title', 'creator', 'last_update',) |
123 | list_filter = ('title',) |
124 | ordering = ['-last_update'] |
125 | fieldsets = ( |
126 | @@ -32,11 +32,11 @@ |
127 | |
128 | |
129 | class ChangeSetAdmin(admin.ModelAdmin): |
130 | - search_fields = ['article__title'] |
131 | - list_display = ('article', 'revision', 'old_title', 'old_markup', |
132 | - 'editor', 'editor_ip', 'reverted', 'modified', |
133 | + search_fields = ['old_title'] |
134 | + list_display = ('article', 'old_title', 'old_markup', |
135 | + 'editor', 'reverted', 'modified', |
136 | 'comment') |
137 | - list_filter = ('old_title', 'content_diff') |
138 | + list_filter = ('article__title',) |
139 | ordering = ('-modified',) |
140 | fieldsets = ( |
141 | ('Article', {'fields': ('article',)}), |
142 | |
143 | === modified file 'wiki/forms.py' |
144 | --- wiki/forms.py 2016-12-13 18:28:51 +0000 |
145 | +++ wiki/forms.py 2017-01-18 20:19:31 +0000 |
146 | @@ -2,11 +2,11 @@ |
147 | import re |
148 | |
149 | from django import forms |
150 | -from django.forms import widgets |
151 | from django.contrib.contenttypes.models import ContentType |
152 | from django.utils.translation import ugettext_lazy as _ |
153 | |
154 | from wiki.models import Article |
155 | +from wiki.models import ChangeSet |
156 | from wiki.templatetags.wiki_extras import WIKI_WORD_RE |
157 | |
158 | wikiword_pattern = re.compile('^' + WIKI_WORD_RE + '$') |
159 | @@ -34,11 +34,31 @@ |
160 | 'group', 'created_at', 'last_update') |
161 | |
162 | def clean_title(self): |
163 | - """Page title must be a WikiWord.""" |
164 | + """Check for some errors regarding the title: |
165 | + |
166 | + 1. Check for bad characters |
167 | + 2. Check for already used titles |
168 | + |
169 | + Immediately trying to change the title of a new article to an existing title |
170 | + is handled on the database level. |
171 | + |
172 | + """ |
173 | + |
174 | title = self.cleaned_data['title'] |
175 | if not wikiword_pattern.match(title): |
176 | - raise forms.ValidationError(_('Must be a WikiWord.')) |
177 | - |
178 | + raise forms.ValidationError( |
179 | + _('Only alphanumeric characters, blank spaces and the underscore are allowed in a title.')) |
180 | + |
181 | + # 'self.initial' contains the prefilled values of the form |
182 | + pre_title = self.initial.get('title', None) |
183 | + if pre_title != title or not pre_title: |
184 | + # Check if the new name has been used already |
185 | + cs = ChangeSet.objects.filter(old_title=title) |
186 | + if cs: |
187 | + raise forms.ValidationError( |
188 | + _('The title %(title)s is already in use, maybe an other article used to have this name.'), params={'title': title},) |
189 | + |
190 | + # title not changed, no errors |
191 | return title |
192 | |
193 | def clean(self): |
194 | @@ -52,10 +72,6 @@ |
195 | kw['object_id'] = self.cleaned_data['object_id'] |
196 | except KeyError: |
197 | pass # some error in this fields |
198 | - else: |
199 | - if Article.objects.filter(**kw).count(): |
200 | - raise forms.ValidationError( |
201 | - _('An article with this title already exists.')) |
202 | |
203 | return self.cleaned_data |
204 | |
205 | |
206 | === added file 'wiki/migrations/0002_auto_20161218_1056.py' |
207 | --- wiki/migrations/0002_auto_20161218_1056.py 1970-01-01 00:00:00 +0000 |
208 | +++ wiki/migrations/0002_auto_20161218_1056.py 2017-01-18 20:19:31 +0000 |
209 | @@ -0,0 +1,19 @@ |
210 | +# -*- coding: utf-8 -*- |
211 | +from __future__ import unicode_literals |
212 | + |
213 | +from django.db import models, migrations |
214 | + |
215 | + |
216 | +class Migration(migrations.Migration): |
217 | + |
218 | + dependencies = [ |
219 | + ('wiki', '0001_initial'), |
220 | + ] |
221 | + |
222 | + operations = [ |
223 | + migrations.AlterField( |
224 | + model_name='article', |
225 | + name='title', |
226 | + field=models.CharField(unique=True, max_length=50, verbose_name='Title'), |
227 | + ), |
228 | + ] |
229 | |
230 | === modified file 'wiki/models.py' |
231 | --- wiki/models.py 2016-12-13 18:28:51 +0000 |
232 | +++ wiki/models.py 2017-01-18 20:19:31 +0000 |
233 | @@ -48,8 +48,8 @@ |
234 | |
235 | |
236 | class Article(models.Model): |
237 | - """A wiki page.""" |
238 | - title = models.CharField(_(u"Title"), max_length=50) |
239 | + """A wiki page reflecting the actual revision.""" |
240 | + title = models.CharField(_(u"Title"), max_length=50, unique=True) |
241 | content = models.TextField(_(u"Content")) |
242 | summary = models.CharField(_(u"Summary"), max_length=150, |
243 | null=True, blank=True) |
244 | |
245 | === modified file 'wiki/urls.py' |
246 | --- wiki/urls.py 2016-12-13 18:28:51 +0000 |
247 | +++ wiki/urls.py 2017-01-18 20:19:31 +0000 |
248 | @@ -56,4 +56,7 @@ |
249 | |
250 | url(r'^history/(?P<title>' + WIKI_URL_RE + r')/revert/$', views.revert_to_revision, |
251 | name='wiki_revert_to_revision'), |
252 | + |
253 | + url(r'^backlinks/(?P<title>' + WIKI_URL_RE + r')/$', views.backlinks, |
254 | + name='backlinks'), |
255 | ] |
256 | |
257 | === modified file 'wiki/views.py' |
258 | --- wiki/views.py 2016-12-13 18:28:51 +0000 |
259 | +++ wiki/views.py 2017-01-18 20:19:31 +0000 |
260 | @@ -10,7 +10,7 @@ |
261 | HttpResponseNotAllowed, HttpResponse, HttpResponseForbidden) |
262 | from django.shortcuts import get_object_or_404, render_to_response, redirect |
263 | from django.contrib.contenttypes.models import ContentType |
264 | - |
265 | +from django.contrib import messages |
266 | from wiki.forms import ArticleForm |
267 | from wiki.models import Article, ChangeSet, dmp |
268 | |
269 | @@ -19,6 +19,7 @@ |
270 | from mainpage.templatetags.wl_markdown import do_wl_markdown |
271 | |
272 | from wl_utils import get_real_ip |
273 | +import re |
274 | |
275 | # Settings |
276 | # lock duration in minutes |
277 | @@ -260,29 +261,26 @@ |
278 | return HttpResponseForbidden() |
279 | |
280 | try: |
281 | + # Try to fetch an existing article |
282 | article = article_qs.get(**article_args) |
283 | except ArticleClass.DoesNotExist: |
284 | - article = None |
285 | + # No article found, maybe we have a redirect |
286 | + try: |
287 | + cs = ChangeSet.objects.filter(old_title=title)[0] |
288 | + article = article_qs.get(title=cs.article) |
289 | + except IndexError: |
290 | + # No Article found and no redirect found |
291 | + article = None |
292 | |
293 | if request.method == 'POST': |
294 | |
295 | form = ArticleFormClass(request.POST, instance=article) |
296 | - |
297 | + |
298 | form.cache_old_content() |
299 | if form.is_valid(): |
300 | |
301 | - # NOCOMM Franku: This has never worked as i know and is IMHO |
302 | - # useless. This code works with django 1.8 but misses some code |
303 | - # in template. See |
304 | - # https://docs.djangoproject.com/en/1.8/ref/contrib/messages/#module-django.contrib.messages |
305 | - |
306 | if request.user.is_authenticated(): |
307 | form.editor = request.user |
308 | - # if article is None: |
309 | - # user_message = u"Your article was created successfully." |
310 | - # else: |
311 | - # user_message = u"Your article was edited successfully." |
312 | - # messages.success(request, user_message) |
313 | |
314 | if ((article is None) and (group_slug is not None)): |
315 | form.group = group |
316 | @@ -478,18 +476,23 @@ |
317 | |
318 | article = get_object_or_404(article_qs, **article_args) |
319 | |
320 | - if request.user.is_authenticated(): |
321 | - article.revert_to(revision, get_real_ip(request), request.user) |
322 | - else: |
323 | - article.revert_to(revision, get_real_ip(request)) |
324 | - |
325 | - # NOCOMM Franku: This has never worked as i know and is IMHO |
326 | - # useless. If we want this it has to be fixed for django 1.8 |
327 | - # See comment in edit_article() |
328 | - # if request.user.is_authenticated(): |
329 | - # request.user.message_set.create( |
330 | - # message=u"The article was reverted successfully.") |
331 | - |
332 | + |
333 | + # Check whether there is another Article with the same name to which this article |
334 | + # wants to be reverted to. If so: prevent it and show a message. |
335 | + old_title = article.changeset_set.filter( |
336 | + revision=revision+1).get().old_title |
337 | + try: |
338 | + art = Article.objects.exclude(pk=article.pk).get(title=old_title) |
339 | + except Article.DoesNotExist: |
340 | + # No existing article found -> reverting possible |
341 | + if request.user.is_authenticated(): |
342 | + article.revert_to(revision, get_real_ip(request), request.user) |
343 | + else: |
344 | + article.revert_to(revision, get_real_ip(request)) |
345 | + return redirect(article) |
346 | + # An article with this name exists |
347 | + messages.error( |
348 | + request, 'Reverting not possible because an article with name \'%s\' already exists' % old_title) |
349 | return redirect(article) |
350 | |
351 | return HttpResponseNotAllowed(['POST']) |
352 | @@ -622,3 +625,50 @@ |
353 | dmp.diff_cleanupSemantic(diffs) |
354 | |
355 | return HttpResponse(dmp.diff_prettyHtml(diffs), content_type='text/html') |
356 | + |
357 | + |
358 | +def backlinks(request, title): |
359 | + """Simple text search for links in other wiki articles pointing to the |
360 | + current article. |
361 | + |
362 | + If we convert WikiWords to markdown wikilinks syntax, this view |
363 | + should be changed to use '[[title]]' for searching. |
364 | + |
365 | + """ |
366 | + |
367 | + # Find old title(s) of this article |
368 | + this_article = Article.objects.get(title=title) |
369 | + changesets = this_article.changeset_set.all() |
370 | + old_titles = [] |
371 | + for cs in changesets: |
372 | + if cs.old_title and cs.old_title != title and cs.old_title not in old_titles: |
373 | + old_titles.append(cs.old_title) |
374 | + |
375 | + # Differentiate between WikiWords and other |
376 | + m = re.match(r"(!?)(\b[A-Z][a-z]+[A-Z]\w+\b)", title) |
377 | + if m: |
378 | + # title is a 'WikiWord' -> This catches also 'MingW' but we have no such title |
379 | + search_title = re.compile(r"%s" % title) |
380 | + else: |
381 | + # Others must be written like links: '[Wiki Page](/wiki/Wiki Page)' |
382 | + search_title = re.compile(r"\/%s\)" % title) |
383 | + |
384 | + # Search for current and previous titles |
385 | + found_old_links = [] |
386 | + found_links = [] |
387 | + articles_all = Article.objects.all().exclude(title=title) |
388 | + for article in articles_all: |
389 | + match = search_title.search(article.content) |
390 | + if match: |
391 | + found_links.append({'title': article.title}) |
392 | + |
393 | + for old_title in old_titles: |
394 | + if old_title in article.content: |
395 | + found_old_links.append({'old_title': old_title, 'title': article.title }) |
396 | + |
397 | + context = {'found_links': found_links, |
398 | + 'found_old_links': found_old_links, |
399 | + 'name': title} |
400 | + return render_to_response('wiki/backlinks.html', |
401 | + context, |
402 | + context_instance=RequestContext(request)) |
> In general i think redirects are not really a good thing at all, because it may lead into many redirects after some time.
While I agree here, they are also sorta required: if some external site links to a site in our Wiki, we want this link to stay valid forever ideally. So we have to track renames (or completely forbid them) and forward to the new page.
About the (internal) redirects: Is it not in our power to fix all internal links that linked to the old page into links that link to the new page? I mean, we can just make a new revision for each page algorithmically that just does the renames. No user interaction is needed.
> If an article is only linked over the main menu, it shows also the message to link this article somewhere.
Could be handled with a custom Django template tag like "WikiLink" that behaves like inter WikiLinking. Not sure if that is really necessary though.