1b16077f409a29a37dbfeca3a42764fa7f1e3a16
[redakcja.git] / apps / wiki / views.py
1 # -*- coding: utf-8 -*-
2 #
3 # This file is part of MIL/PEER, licensed under GNU Affero GPLv3 or later.
4 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
5 #
6 import json
7 import os
8 import logging
9 import urllib
10
11 from django.conf import settings
12 from django import http
13 from django.http import HttpResponseForbidden
14 from django.middleware.gzip import GZipMiddleware
15 from django.utils.decorators import decorator_from_middleware
16 from django.utils.encoding import smart_unicode
17 from django.utils.formats import localize
18 from django.utils.translation import ugettext as _
19 from django.views.decorators.http import require_POST
20 from django.shortcuts import get_object_or_404, render
21
22 from catalogue.models import Document, Template
23 from dvcs.models import Revision
24 import nice_diff
25 from wiki import forms
26 from wiki.helpers import JSONResponse, JSONFormInvalid
27
28 #
29 # Quick hack around caching problems, TODO: use ETags
30 #
31 from django.views.decorators.cache import never_cache
32
33 logger = logging.getLogger("fnp.wiki")
34
35 MAX_LAST_DOCS = 10
36
37
38 def get_history(document):
39     revisions = []
40     for i, revision in enumerate(document.history()):
41         revisions.append({
42             "version": i + 1,
43             "description": revision.description,
44             "author": revision.author_str(),
45             "date": localize(revision.created_at),
46             "revision": revision.pk,
47             "published": _("Published") + ": " +
48             localize(revision.publish_log.order_by('-timestamp')[0].timestamp)
49             if revision.publish_log.exists() else "",
50         })
51     return revisions
52
53
54 @never_cache
55 def editor(request, pk, template_name='wiki/bootstrap.html'):
56     doc = get_object_or_404(Document, pk=pk, deleted=False)
57
58     save_form = forms.DocumentTextSaveForm(user=request.user, prefix="textsave")
59     text = doc.materialize()
60     revision = doc.revision
61     history = get_history(doc)
62     return render(request, template_name, {
63         'serialized_document_data': json.dumps({
64             'document': text,
65             'document_id': doc.pk,
66             'title': doc.meta().get('title', ''),
67             'history': history,
68             'version': len(history),
69             'revision': revision.pk,
70             'stage': doc.stage,
71             'assignment': str(doc.assigned_to),
72         }),
73         'serialized_templates': json.dumps([
74             {'id': t.id, 'name': t.name, 'content': t.content} for t in Template.objects.filter(is_partial=True)
75         ]),
76         'forms': {
77             "text_save": save_form,
78             "text_revert": forms.DocumentTextRevertForm(prefix="textrevert"),
79             "text_publish": forms.DocumentTextPublishForm(prefix="textpublish"),
80         },
81         'pk': doc.pk,
82     })
83
84
85 @never_cache
86 @decorator_from_middleware(GZipMiddleware)
87 def text(request, doc_id):
88     doc = get_object_or_404(Document, pk=doc_id, deleted=False)
89     # if not doc.book.accessible(request):
90     #     return HttpResponseForbidden("Not authorized.")
91
92     if request.method == 'POST':
93         form = forms.DocumentTextSaveForm(request.POST, user=request.user, prefix="textsave")
94         if form.is_valid():
95             if request.user.is_authenticated():
96                 author = request.user
97             else:
98                 author = None
99             text = form.cleaned_data['text']
100             # parent_revision = form.cleaned_data['parent_revision']
101             # if parent_revision is not None:
102             #     parent = doc.at_revision(parent_revision)
103             # else:
104             #     parent = None
105             stage = form.cleaned_data['stage']
106             # tags = [stage] if stage else []
107             # publishable = (form.cleaned_data['publishable'] and
108             #                request.user.has_perm('catalogue.can_pubmark'))
109             try:
110                 doc.commit(
111                     author=author,
112                     text=text,
113                     description=form.cleaned_data['comment'],
114                     author_name=form.cleaned_data['author_name'],
115                     author_email=form.cleaned_data['author_email'],
116                 )
117                 doc.set_stage(stage)
118             except:
119                 from traceback import print_exc
120                 print_exc()
121                 raise
122             # revision = doc.revision()
123             return JSONResponse({
124                 'text': None,  # doc.materialize() if parent_revision != revision else None,
125                 # 'version': revision,
126                 # 'stage': doc.stage.name if doc.stage else None,
127                 'assignment': doc.assigned_to.username if doc.assigned_to else None
128             })
129         else:
130             return JSONFormInvalid(form)
131     else:
132         revision = request.GET.get("revision", None)
133         
134         try:
135             revision = int(revision)
136         except (ValueError, TypeError):
137             revision = doc.revision()
138
139         if revision is not None:
140             text = doc.at_revision(revision).materialize()
141         else:
142             text = ''
143
144         return JSONResponse({
145             'text': text,
146             'meta': {},
147             'revision': revision,
148         })
149
150
151 @never_cache
152 @require_POST
153 def revert(request, doc_id):
154     form = forms.DocumentTextRevertForm(request.POST, prefix="textrevert")
155     if form.is_valid():
156         doc = get_object_or_404(Document, pk=doc_id, deleted=False)
157         rev = get_object_or_404(Revision, pk=form.cleaned_data['revision'])
158
159         comment = form.cleaned_data['comment']
160         comment += "\n#revert to %s" % rev.pk
161
162         if request.user.is_authenticated():
163             author = request.user
164         else:
165             author = None
166
167         # before = doc.revision
168         logger.info("Reverting %s to %s", doc_id, rev.pk)
169
170         doc.commit(
171             author=author,
172             text=rev.materialize(),
173             description=comment,
174             # author_name=form.cleaned_data['author_name'], #?
175             # author_email=form.cleaned_data['author_email'], #?
176         )
177
178         return JSONResponse({
179             # 'document': None, #doc.materialize() if before != doc.revision else None,
180             # 'version': doc.revision(),
181         })
182     else:
183         return JSONFormInvalid(form)
184
185
186 @never_cache
187 def gallery(request, directory):
188     if not request.user.is_authenticated():
189         return HttpResponseForbidden("Not authorized.")
190
191     try:
192         base_url = ''.join((
193                         smart_unicode(settings.MEDIA_URL),
194                         smart_unicode(settings.IMAGE_DIR),
195                         smart_unicode(directory)))
196
197         base_dir = os.path.join(
198                     smart_unicode(settings.MEDIA_ROOT),
199                     smart_unicode(settings.IMAGE_DIR),
200                     smart_unicode(directory))
201
202         def map_to_url(filename):
203             return urllib.quote("%s/%s" % (base_url, smart_unicode(filename)))
204
205         def is_image(filename):
206             return os.path.splitext(f)[1].lower() in (u'.jpg', u'.jpeg', u'.png')
207
208         images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
209         images.sort()
210
211         return JSONResponse(images)
212     except (IndexError, OSError):
213         logger.exception("Unable to fetch gallery")
214         raise http.Http404
215
216
217 @never_cache
218 def diff(request, doc_id):
219     revA = int(request.GET.get('from', 0))
220     revB = int(request.GET.get('to', 0))
221
222     if revA > revB:
223         revA, revB = revB, revA
224
225     if revB == 0:
226         revB = None
227
228     # TODO: check if revisions in line.
229
230     doc = get_object_or_404(Document, pk=doc_id, deleted=False)
231
232     # allow diff from the beginning
233     if revA:
234         docA = Revision.objects.get(pk=revA).materialize()
235     else:
236         docA = ""
237     docB = Revision.objects.get(pk=revB).materialize()
238
239     return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(), docB.splitlines(), context=3))
240
241
242 @never_cache
243 def history(request, doc_id):
244     # TODO: pagination
245     doc = get_object_or_404(Document, pk=doc_id, deleted=False)
246
247     return JSONResponse(get_history(doc))