~widelands-dev/widelands-website/trunk

« back to all changes in this revision

Viewing changes to wiki/views.py

  • Committer: Holger Rapp
  • Date: 2019-06-21 18:34:42 UTC
  • mfrom: (540.1.3 update_ops_script)
  • Revision ID: sirver@gmx.de-20190621183442-y2ulybzr0rdvfefd
Adapt the update script for the new server.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
from django.conf import settings
6
6
from django.core.cache import cache
7
7
from django.template import RequestContext
8
 
from django.core.urlresolvers import reverse
 
8
from django.urls import reverse
9
9
from django.http import (Http404, HttpResponseRedirect,
10
10
                         HttpResponseNotAllowed, HttpResponse, HttpResponseForbidden)
11
 
from django.shortcuts import get_object_or_404, render_to_response
12
 
from django.views.generic.simple import redirect_to
13
 
from django.utils.translation import ugettext_lazy as _
 
11
from django.shortcuts import get_object_or_404, render, redirect
14
12
from django.contrib.contenttypes.models import ContentType
15
 
from django.contrib.syndication.feeds import FeedDoesNotExist
 
13
from django.contrib import messages
16
14
 
17
15
from wiki.forms import ArticleForm
18
16
from wiki.models import Article, ChangeSet, dmp
19
 
from wiki.feeds import (RssArticleHistoryFeed, AtomArticleHistoryFeed,
20
 
                        RssHistoryFeed, AtomHistoryFeed)
 
17
 
21
18
from wiki.utils import get_ct
22
19
from django.contrib.auth.decorators import login_required
23
 
 
24
20
from mainpage.templatetags.wl_markdown import do_wl_markdown
 
21
from markdownextensions.semanticwikilinks.mdx_semanticwikilinks import WIKILINK_RE
 
22
 
 
23
from mainpage.wl_utils import get_real_ip
 
24
from mainpage.wl_utils import get_valid_cache_key
 
25
 
 
26
import re
 
27
import urllib.request, urllib.parse, urllib.error
25
28
 
26
29
# Settings
27
30
#  lock duration in minutes
40
43
ALL_CHANGES = ChangeSet.objects.all()
41
44
 
42
45
 
43
 
def get_real_ip(request):
44
 
    """ Returns the real user IP, even if behind a proxy.
45
 
    Set BEHIND_PROXY to True in your settings if Django is
46
 
    running behind a proxy.
47
 
    """
48
 
    if getattr(settings, 'BEHIND_PROXY', False):
49
 
        return request.META['HTTP_X_FORWARDED_FOR']
50
 
    return request.META['REMOTE_ADDR']
51
 
 
52
46
def get_articles_by_group(article_qs, group_slug=None,
53
47
                          group_slug_field=None, group_qs=None):
54
48
    group = None
59
53
                                       object_id=group.id)
60
54
    return article_qs, group
61
55
 
 
56
 
62
57
def get_articles_for_object(object, article_qs=None):
63
58
    if article_qs is None:
64
59
        article_qs = ALL_ARTICLES
65
 
    return article_qs.filter( content_type=get_ct(object),
66
 
                                       object_id=object.id)
 
60
    return article_qs.filter(content_type=get_ct(object),
 
61
                             object_id=object.id)
 
62
 
67
63
 
68
64
def get_url(urlname, group=None, args=None, kw=None):
69
65
    if group is None:
72
68
        app = group._meta.app_label
73
69
        urlconf = '.'.join([app, 'urls'])
74
70
        url = reverse(urlname, urlconf, kwargs=kw)
75
 
        return ''.join(['/', app, url]) # @@@ harcoded: /app/.../
 
71
        return ''.join(['/', app, url])  # @@@ harcoded: /app/.../
76
72
 
77
73
 
78
74
class ArticleEditLock(object):
79
 
    """ A soft lock to edting an article.
80
 
    """
81
 
 
 
75
    """A soft lock to edting an article."""
82
76
    def __init__(self, title, request, message_template=None):
83
77
        self.title = title
84
 
        self.user_ip = get_real_ip(request)
85
 
        self.created_at = datetime.now()
 
78
        self.user = request.user
 
79
        self.created_at = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
86
80
 
87
81
        if message_template is None:
88
82
            message_template = ('Possible edit conflict:'
89
 
            ' another user started editing this article at %s')
 
83
                                ' Another user started editing this article at %s')
90
84
 
91
85
        self.message_template = message_template
92
 
 
93
 
        cache.set(title, self, WIKI_LOCK_DURATION*60)
 
86
        cache.set(title, self, WIKI_LOCK_DURATION * 60)
94
87
 
95
88
    def create_message(self, request):
96
 
        """ Send a message to the user if there is another user
97
 
        editing this article.
98
 
        """
 
89
        """Show a message to the user if there is another user editing this
 
90
        article."""
99
91
        if not self.is_mine(request):
100
92
            user = request.user
101
 
            user.message_set.create(
102
 
                message=self.message_template%self.created_at)
 
93
            messages.add_message(request,
 
94
                                 messages.INFO,
 
95
                                 self.message_template % self.created_at)
103
96
 
104
97
    def is_mine(self, request):
105
 
        return self.user_ip == get_real_ip(request)
 
98
        return self.user == request.user
106
99
 
107
100
 
108
101
def has_read_perm(user, group, is_member, is_private):
115
108
        return False
116
109
    return True
117
110
 
 
111
 
118
112
def has_write_perm(user, group, is_member):
119
 
    """ Return True if the user have permission to edit Articles,
120
 
    False otherwise.
121
 
    """
 
113
    """Return True if the user have permission to edit Articles, False
 
114
    otherwise."""
122
115
    if (group is None) or (is_member is None) or is_member(user, group):
123
116
        return True
124
117
    return False
152
145
 
153
146
        if group_slug is not None:
154
147
            template_params['group'] = group
155
 
            new_article = ArticleClass(title="NewArticle",
 
148
            new_article = ArticleClass(title='NewArticle',
156
149
                                       content_type=get_ct(group),
157
150
                                       object_id=group.id)
158
151
        else:
159
 
            new_article = ArticleClass(title="NewArticle")
 
152
            new_article = ArticleClass(title='NewArticle')
160
153
        template_params['new_article'] = new_article
161
154
        if extra_context is not None:
162
155
            template_params.update(extra_context)
163
156
 
164
 
        return render_to_response('/'.join([template_dir, template_name]),
165
 
                                  template_params,
166
 
                                  context_instance=RequestContext(request))
 
157
        return render(request, '/'.join([template_dir, template_name]),
 
158
                                  template_params,)
167
159
    return HttpResponseNotAllowed(['GET'])
168
160
 
169
161
 
170
 
def view_article(request, title,
171
 
                 ArticleClass=Article, # to create an unsaved instance
 
162
def view_article(request, title, revision=None,
 
163
                 ArticleClass=Article,  # to create an unsaved instance
172
164
                 group_slug=None, group_slug_field=None, group_qs=None,
173
165
                 article_qs=ALL_ARTICLES,
174
166
                 template_name='view.html',
181
173
    if request.method == 'GET':
182
174
        article_args = {'title': title}
183
175
        if group_slug is not None:
184
 
            group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
176
            group = get_object_or_404(
 
177
                group_qs, **{group_slug_field: group_slug})
185
178
            article_args.update({'content_type': get_ct(group),
186
179
                                 'object_id': group.id})
187
180
            allow_read = has_read_perm(request.user, group, is_member,
193
186
        if not allow_read:
194
187
            return HttpResponseForbidden()
195
188
 
 
189
        is_observing = False
 
190
        redirected_from = None
196
191
        try:
197
192
            article = article_qs.get(**article_args)
198
193
            if notification is not None:
199
194
                is_observing = notification.is_observing(article, request.user)
200
 
            else:
201
 
                is_observing = False
202
195
        except ArticleClass.DoesNotExist:
203
 
            article = ArticleClass(**article_args)
204
 
            is_observing = False
 
196
            try:
 
197
                # try to find an article that once had this title
 
198
                article = ChangeSet.objects.filter(
 
199
                    old_title=title).order_by('-revision')[0].article
 
200
                redirected_from = title
 
201
                # if article is not None:
 
202
                #    return redirect(article, permanent=True)
 
203
            except IndexError:
 
204
                article = ArticleClass(**article_args)
 
205
 
 
206
        if revision is not None:
 
207
            changeset = get_object_or_404(
 
208
                article.changeset_set, revision=revision)
 
209
            article.content = changeset.get_content()
205
210
 
206
211
        template_params = {'article': article,
 
212
                           'revision': revision,
 
213
                           'redirected_from': redirected_from,
207
214
                           'allow_write': allow_write}
208
215
 
209
216
        if notification is not None:
215
222
        if extra_context is not None:
216
223
            template_params.update(extra_context)
217
224
 
218
 
        return render_to_response('/'.join([template_dir, template_name]),
219
 
                                  template_params,
220
 
                                  context_instance=RequestContext(request))
 
225
        return render(request, '/'.join([template_dir, template_name]),
 
226
                                  template_params,)
221
227
    return HttpResponseNotAllowed(['GET'])
222
228
 
223
229
 
225
231
def edit_article(request, title,
226
232
                 group_slug=None, group_slug_field=None, group_qs=None,
227
233
                 article_qs=ALL_ARTICLES,
228
 
                 ArticleClass=Article, # to get the DoesNotExist exception
 
234
                 ArticleClass=Article,  # to get the DoesNotExist exception
229
235
                 ArticleFormClass=ArticleForm,
230
236
                 template_name='edit.html',
231
237
                 template_dir='wiki',
238
244
    group = None
239
245
    article_args = {'title': title}
240
246
    if group_slug is not None:
241
 
        group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
247
        group = get_object_or_404(group_qs, **{group_slug_field: group_slug})
242
248
        group_ct = get_ct(group)
243
249
        article_args.update({'content_type': group_ct,
244
250
                             'object_id': group.id})
252
258
        return HttpResponseForbidden()
253
259
 
254
260
    try:
 
261
        # Try to fetch an existing article
255
262
        article = article_qs.get(**article_args)
256
263
    except ArticleClass.DoesNotExist:
257
 
        article = None
 
264
        # No article found, maybe we have a redirect
 
265
        try:
 
266
            cs = ChangeSet.objects.filter(old_title=title)[0]
 
267
            article = article_qs.get(title=cs.article)
 
268
        except IndexError:
 
269
            # No Article found and no redirect found
 
270
            article = None
258
271
 
259
272
    if request.method == 'POST':
260
273
 
261
274
        form = ArticleFormClass(request.POST, instance=article)
262
 
 
 
275
        
263
276
        form.cache_old_content()
264
277
        if form.is_valid():
265
 
            if request.user.is_authenticated():
 
278
 
 
279
            if request.user.is_authenticated:
266
280
                form.editor = request.user
267
 
                if article is None:
268
 
                    user_message = u"Your article was created successfully."
269
 
                else:
270
 
                    user_message = u"Your article was edited successfully."
271
 
                request.user.message_set.create(message=user_message)
272
281
 
273
282
            if ((article is None) and (group_slug is not None)):
274
283
                form.group = group
275
284
 
276
285
            new_article, changeset = form.save()
277
286
 
278
 
            url = get_url('wiki_article', group,
279
 
                          [new_article.title],
280
 
                          {'title': new_article.title,
281
 
                           'group_slug': group_slug})
282
 
 
283
 
            return redirect_to(request, url)
 
287
            lock = cache.get(get_valid_cache_key(title))
 
288
            if lock is not None:
 
289
                # Clean the lock
 
290
                cache.delete(get_valid_cache_key(title))
 
291
            
 
292
            if notification and not changeset.reverted:
 
293
                # Get observers for this article and exclude current editor
 
294
                items = notification.ObservedItem.objects.all_for(
 
295
                    new_article, 'post_save').exclude(user=request.user).iterator()
 
296
                users = [o.user for o in items]
 
297
                notification.send(users, 'wiki_observed_article_changed',
 
298
                                  {'editor': request.user,
 
299
                                   'rev': changeset.revision,
 
300
                                   'rev_comment': changeset.comment,
 
301
                                   'article': new_article})
 
302
 
 
303
 
 
304
            return redirect(new_article)
284
305
 
285
306
    elif request.method == 'GET':
286
 
        user_ip = get_real_ip(request)
287
 
 
288
 
        lock = cache.get(title, None)
 
307
        lock = cache.get(get_valid_cache_key(title))
289
308
        if lock is None:
290
 
            lock = ArticleEditLock(title, request)
 
309
            lock = ArticleEditLock(get_valid_cache_key(title), request)
291
310
        lock.create_message(request)
292
 
 
293
 
        initial = {'user_ip': user_ip}
 
311
        initial = {}
294
312
        if group_slug is not None:
295
313
            initial.update({'content_type': group_ct.id,
296
314
                            'object_id': group.id})
303
321
            initial['action'] = 'edit'
304
322
            form = ArticleFormClass(instance=article,
305
323
                                    initial=initial)
306
 
 
307
324
    if not article:
308
 
        template_params = {'form': form, "new_article": True }
 
325
        template_params = {'form': form, 'new_article': True}
309
326
    else:
310
 
        template_params = {'form': form, "new_article": False,
311
 
            "content_type": ContentType.objects.get_for_model(Article).pk, "object_id": article.pk,
312
 
            "images": article.all_images(),
313
 
            "article": article,
314
 
        }
 
327
        template_params = {'form': form, 'new_article': False,
 
328
                           'content_type': ContentType.objects.get_for_model(Article).pk, 'object_id': article.pk,
 
329
                           'images': article.all_images(),
 
330
                           'article': article,
 
331
                           }
315
332
 
316
333
    if group_slug is not None:
317
334
        template_params['group'] = group
318
335
    if extra_context is not None:
319
336
        template_params.update(extra_context)
320
337
 
321
 
    return render_to_response('/'.join([template_dir, template_name]),
322
 
                              template_params,
323
 
                              context_instance=RequestContext(request))
 
338
    return render(request, '/'.join([template_dir, template_name]),
 
339
                              template_params,)
324
340
 
325
341
 
326
342
def view_changeset(request, title, revision,
 
343
                   revision_from=None,
327
344
                   group_slug=None, group_slug_field=None, group_qs=None,
328
345
                   article_qs=ALL_ARTICLES,
329
346
                   changes_qs=ALL_CHANGES,
334
351
                   is_private=None,
335
352
                   *args, **kw):
336
353
 
337
 
    if request.method == "GET":
 
354
    if request.method == 'GET':
338
355
        article_args = {'article__title': title}
339
356
        if group_slug is not None:
340
 
            group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
357
            group = get_object_or_404(
 
358
                group_qs, **{group_slug_field: group_slug})
341
359
            article_args.update({'article__content_type': get_ct(group),
342
360
                                 'article__object_id': group.id})
343
361
        changeset = get_object_or_404(
347
365
 
348
366
        article_args = {'title': title}
349
367
        if group_slug is not None:
350
 
            group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
368
            group = get_object_or_404(
 
369
                group_qs, **{group_slug_field: group_slug})
351
370
            article_args.update({'content_type': get_ct(group),
352
371
                                 'object_id': group.id})
353
372
            allow_read = has_read_perm(request.user, group, is_member,
361
380
 
362
381
        article = article_qs.get(**article_args)
363
382
 
 
383
        if revision_from is None:
 
384
            revision_from = int(revision) - 1
 
385
 
 
386
        from_value = None
 
387
        if int(revision) is not int(revision_from) + 1:
 
388
            from_value = revision_from
 
389
 
364
390
        template_params = {'article': article,
365
391
                           'article_title': article.title,
366
392
                           'changeset': changeset,
 
393
                           'differences': changeset.compare_to(revision_from),
 
394
                           'from': from_value,
 
395
                           'to': revision,
367
396
                           'allow_write': allow_write}
368
397
 
369
398
        if group_slug is not None:
371
400
        if extra_context is not None:
372
401
            template_params.update(extra_context)
373
402
 
374
 
        return render_to_response('/'.join([template_dir, template_name]),
375
 
                                  template_params,
376
 
                                  context_instance=RequestContext(request))
 
403
        return render(request, '/'.join([template_dir, template_name]),
 
404
                                  template_params,)
377
405
    return HttpResponseNotAllowed(['GET'])
378
406
 
379
407
 
391
419
 
392
420
        article_args = {'title': title}
393
421
        if group_slug is not None:
394
 
            group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
422
            group = get_object_or_404(
 
423
                group_qs, **{group_slug_field: group_slug})
395
424
            article_args.update({'content_type': get_ct(group),
396
425
                                 'object_id': group.id})
397
426
            allow_read = has_read_perm(request.user, group, is_member,
404
433
            return HttpResponseForbidden()
405
434
 
406
435
        article = get_object_or_404(article_qs, **article_args)
407
 
        changes = article.changeset_set.filter(
408
 
            reverted=False).order_by('-revision')
 
436
        # changes = article.changeset_set.filter(
 
437
        #    reverted=False).order_by('-revision')
 
438
        changes = article.changeset_set.all().order_by('-revision')
409
439
 
410
440
        template_params = {'article': article,
411
441
                           'changes': changes,
415
445
        if extra_context is not None:
416
446
            template_params.update(extra_context)
417
447
 
418
 
        return render_to_response('/'.join([template_dir, template_name]),
419
 
                                  template_params,
420
 
                                  context_instance=RequestContext(request))
 
448
        return render(request, '/'.join([template_dir, template_name]),
 
449
                                  template_params,)
421
450
 
422
451
    return HttpResponseNotAllowed(['GET'])
423
452
 
439
468
 
440
469
        group = None
441
470
        if group_slug is not None:
442
 
            group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
471
            group = get_object_or_404(
 
472
                group_qs, **{group_slug_field: group_slug})
443
473
            article_args.update({'content_type': get_ct(group),
444
474
                                 'object_id': group.id})
445
475
            allow_read = has_read_perm(request.user, group, is_member,
453
483
 
454
484
        article = get_object_or_404(article_qs, **article_args)
455
485
 
456
 
        if request.user.is_authenticated():
457
 
            article.revert_to(revision, get_real_ip(request), request.user)
458
 
        else:
459
 
            article.revert_to(revision, get_real_ip(request))
460
 
 
461
 
 
462
 
        if request.user.is_authenticated():
463
 
            request.user.message_set.create(
464
 
                message=u"The article was reverted successfully.")
465
 
 
466
 
        url = get_url('wiki_article_history', group,
467
 
                      [title], {'title': title,
468
 
                                'group_slug': group_slug})
469
 
 
470
 
        return redirect_to(request, url)
 
486
        
 
487
        # Check whether there is another Article with the same name to which this article
 
488
        # wants to be reverted to. If so: prevent it and show a message.
 
489
        old_title = article.changeset_set.filter(
 
490
            revision=revision+1).get().old_title
 
491
        try:
 
492
            art = Article.objects.exclude(pk=article.pk).get(title=old_title)
 
493
        except Article.DoesNotExist:
 
494
            # No existing article found -> reverting possible
 
495
            if request.user.is_authenticated:
 
496
                article.revert_to(revision, request.user)
 
497
            else:
 
498
                article.revert_to(revision)
 
499
            return redirect(article)
 
500
        # An article with this name exists
 
501
        messages.error(
 
502
            request, 'Reverting not possible because an article with name \'%s\' already exists' % old_title)
 
503
        return redirect(article)
471
504
 
472
505
    return HttpResponseNotAllowed(['POST'])
473
506
 
481
514
            *args, **kw):
482
515
 
483
516
    if request.method == 'GET':
484
 
        if  group_slug is not None:
 
517
        if group_slug is not None:
485
518
            group = get_object_or_404(group_qs,
486
 
                                      **{group_slug_field : group_slug})
 
519
                                      **{group_slug_field: group_slug})
487
520
            changes_qs = changes_qs.filter(article__content_type=get_ct(group),
488
521
                                           article__object_id=group.id)
489
522
            allow_read = has_read_perm(request.user, group, is_member,
503
536
        if extra_context is not None:
504
537
            template_params.update(extra_context)
505
538
 
506
 
        return render_to_response('/'.join([template_dir, template_name]),
507
 
                                  template_params,
508
 
                                  context_instance=RequestContext(request))
 
539
        return render(request, '/'.join([template_dir, template_name]),
 
540
                                  template_params,)
509
541
    return HttpResponseNotAllowed(['GET'])
510
542
 
511
543
 
522
554
    article_args = {'title': title}
523
555
    group = None
524
556
    if group_slug is not None:
525
 
        group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
557
        group = get_object_or_404(group_qs, **{group_slug_field: group_slug})
526
558
        article_args.update({'content_type': get_ct(group),
527
559
                             'object_id': group.id})
528
560
        allow_read = has_read_perm(request.user, group, is_member,
537
569
 
538
570
    if not notification.is_observing(article, request.user):
539
571
        notification.observe(article, request.user,
540
 
           'wiki_observed_article_changed')
541
 
 
542
 
    url = get_url('wiki_article', group,
543
 
                  [article.title], {'title': article.title,
544
 
                                    'group_slug': group_slug})
545
 
 
546
 
    return redirect_to(request, url)
 
572
                             'wiki_observed_article_changed')
 
573
 
 
574
    return redirect(article)
547
575
 
548
576
    return HttpResponseNotAllowed(['POST'])
549
577
 
561
589
    article_args = {'title': title}
562
590
    group = None
563
591
    if group_slug is not None:
564
 
        group = get_object_or_404(group_qs,**{group_slug_field: group_slug})
 
592
        group = get_object_or_404(group_qs, **{group_slug_field: group_slug})
565
593
        article_args.update({'content_type': get_ct(group),
566
594
                             'object_id': group.id})
567
595
        allow_read = has_read_perm(request.user, group, is_member,
577
605
    if notification.is_observing(article, request.user):
578
606
        notification.stop_observing(article, request.user)
579
607
 
580
 
    url = get_url('wiki_article', group,
581
 
                  [article.title], {'title': article.title,
582
 
                                    'group_slug': group_slug})
583
 
 
584
 
    return redirect_to(request, url)
585
 
 
586
 
def article_preview( request ):
587
 
    """
588
 
    This is a AJAX function that previews the body of the
589
 
    article as it is currently displayed.
590
 
 
591
 
    This function is actually pretty simple, it just
592
 
    runs the function through the view template and returns
593
 
    it to the caller
594
 
    """
595
 
    rv = do_wl_markdown( request.POST["body"], safe_mode="escape" )
596
 
    return HttpResponse(rv, mimetype="text/html")
597
 
 
598
 
def article_diff( request ):
599
 
    """
600
 
    This is a AJAX function that diffs the body of the
601
 
    article as it is currently displayed with the current version
602
 
    of the article
603
 
    """
604
 
    current_article = get_object_or_404(Article, pk=int(request.POST["article"]))
605
 
    content = request.POST["body"]
 
608
    return redirect(article)
 
609
 
 
610
 
 
611
def article_preview(request):
 
612
    """This is a AJAX function that previews the body of the article as it is
 
613
    currently displayed.
 
614
 
 
615
    This function is actually pretty simple, it just runs the function
 
616
    through the view template and returns it to the caller
 
617
 
 
618
    """
 
619
    rv = do_wl_markdown(request.POST['body'], 'bleachit')
 
620
    return HttpResponse(rv, content_type='text/html')
 
621
 
 
622
 
 
623
def article_diff(request):
 
624
    """This is a AJAX function that diffs the body of the article as it is
 
625
    currently displayed with the current version of the article."""
 
626
    current_article = get_object_or_404(
 
627
        Article, pk=int(request.POST['article']))
 
628
    content = request.POST['body']
606
629
 
607
630
    diffs = dmp.diff_main(current_article.content, content)
608
 
 
609
 
    return HttpResponse(dmp.diff_prettyHtml(diffs), mimetype="text/html")
610
 
 
611
 
def article_history_feed(request, feedtype, title,
612
 
                         group_slug=None, group_slug_field=None, group_qs=None,
613
 
                         article_qs=ALL_ARTICLES, changes_qs=ALL_CHANGES,
614
 
                         extra_context=None,
615
 
                         is_member=None,
616
 
                         is_private=None,
617
 
                         *args, **kw):
618
 
 
619
 
    feeds = {'rss' : RssArticleHistoryFeed,
620
 
             'atom' : AtomArticleHistoryFeed}
621
 
    ArticleHistoryFeed = feeds.get(feedtype, RssArticleHistoryFeed)
622
 
 
623
 
    try:
624
 
        feedgen = ArticleHistoryFeed(title, request,
625
 
                                     group_slug, group_slug_field, group_qs,
626
 
                                     article_qs, changes_qs,
627
 
                                     extra_context,
628
 
                                     *args, **kw).get_feed(title)
629
 
    except FeedDoesNotExist:
630
 
        raise Http404
631
 
 
632
 
    response = HttpResponse(mimetype=feedgen.mime_type)
633
 
    feedgen.write(response, 'utf-8')
634
 
    return response
635
 
 
636
 
 
637
 
def history_feed(request, feedtype,
638
 
                 group_slug=None, group_slug_field=None, group_qs=None,
639
 
                 article_qs=ALL_ARTICLES, changes_qs=ALL_CHANGES,
640
 
                 extra_context=None,
641
 
                 is_member=None,
642
 
                 is_private=None,
643
 
                 *args, **kw):
644
 
 
645
 
    feeds = {'rss' : RssHistoryFeed,
646
 
             'atom' : AtomHistoryFeed}
647
 
    HistoryFeed = feeds.get(feedtype, RssHistoryFeed)
648
 
 
649
 
    try:
650
 
        feedgen = HistoryFeed(request,
651
 
                              group_slug, group_slug_field, group_qs,
652
 
                              article_qs, changes_qs,
653
 
                              extra_context,
654
 
                              *args, **kw).get_feed()
655
 
    except FeedDoesNotExist:
656
 
        raise Http404
657
 
 
658
 
    response = HttpResponse(mimetype=feedgen.mime_type)
659
 
    feedgen.write(response, 'utf-8')
660
 
    return response
661
 
 
 
631
    dmp.diff_cleanupSemantic(diffs)
 
632
 
 
633
    return HttpResponse(dmp.diff_prettyHtml(diffs), content_type='text/html')
 
634
 
 
635
 
 
636
def backlinks(request, title):
 
637
    """Search for links in other wiki articles pointing to the
 
638
    current article.
 
639
    """
 
640
 
 
641
    # Find old title(s) of this article
 
642
    this_article = get_object_or_404(Article, title=title)
 
643
    changesets = this_article.changeset_set.all()
 
644
    old_titles = []
 
645
    for cs in changesets:
 
646
        if cs.old_title and cs.old_title != title and cs.old_title not in old_titles:
 
647
            old_titles.append(cs.old_title)
 
648
 
 
649
    # Search for semantic wiki links. The regexpr was copied from there
 
650
    # and slightly modified
 
651
    search_title = [re.compile(r"\[\[\s*(%s)/?\s*(\|\s*.+?\s*)?\]\]" % title)]
 
652
 
 
653
    # Search for links in MarkDown syntax, like [Foo](wiki/FooBar)
 
654
    # The regexpr matches the title between '/' and ')'
 
655
    search_title.append(re.compile(r"\/%s\)" % title))
 
656
 
 
657
    # Search for current and previous titles
 
658
    found_old_links = []
 
659
    found_links = []
 
660
    articles_all = Article.objects.all().exclude(title=title)
 
661
    for article in articles_all:
 
662
        for regexp in search_title:
 
663
            # Need to unqoute the content to match
 
664
            # e.g. [[ Back | Title%20of%20Page ]]
 
665
            match = regexp.search(urllib.parse.unquote(article.content))
 
666
            if match:
 
667
                found_links.append({'title': article.title})
 
668
 
 
669
        for old_title in old_titles:
 
670
            if old_title in article.content:
 
671
                found_old_links.append(
 
672
                    {'old_title': old_title, 'title': article.title })
 
673
 
 
674
    context = {'found_links': found_links,
 
675
               'found_old_links': found_old_links,
 
676
               'name': title,
 
677
               'article': this_article,
 
678
               }
 
679
    return render(request, 'wiki/backlinks.html',
 
680
                              context,)