3 logger = logging.getLogger("fnp.wiki")
5 from django.conf import settings
7 from django.views.generic.simple import direct_to_template
8 from django.views.decorators.http import require_POST, require_GET
9 from django.core.urlresolvers import reverse
10 from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
11 ajax_require_permission, recursive_groupby)
12 from django import http
13 from django.shortcuts import get_object_or_404, redirect
15 from wiki.models import Book, Theme
16 from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm
17 from datetime import datetime
18 from django.utils.encoding import smart_unicode
19 from django.utils.translation import ugettext_lazy as _
20 from django.utils.decorators import decorator_from_middleware
21 from django.middleware.gzip import GZipMiddleware
25 # Quick hack around caching problems, TODO: use ETags
27 from django.views.decorators.cache import never_cache
36 def document_list(request):
37 return direct_to_template(request, 'wiki/document_list.html', extra_context={
38 'books': Book.objects.all(),
39 'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
40 key=lambda x: x[1]['time'], reverse=True),
45 def editor(request, slug, template_name='wiki/document_details.html'):
47 book = Book.objects.get(slug=slug)
48 except Book.DoesNotExist:
49 return http.HttpResponseRedirect(reverse("wiki_create_missing", args=[slug]))
51 access_time = datetime.now()
52 last_books = request.session.get("wiki_last_books", {})
58 if len(last_books) > MAX_LAST_DOCS:
59 oldest_key = min(last_books, key=operator.itemgetter('time'))
60 del last_books[oldest_key]
61 request.session['wiki_last_books'] = last_books
63 return direct_to_template(request, template_name, extra_context={
66 "text_save": DocumentTextSaveForm(prefix="textsave"),
67 "text_revert": DocumentTextRevertForm(prefix="textrevert"),
68 "add_tag": DocumentTagForm(prefix="addtag"),
70 'REDMINE_URL': settings.REDMINE_URL,
75 def editor_readonly(request, slug, template_name='wiki/document_details_readonly.html'):
77 book = Book.objects.get(slug=slug)
78 revision = request.GET['revision']
82 access_time = datetime.now()
83 last_books = request.session.get("wiki_last_books", {})
89 if len(last_books) > MAX_LAST_DOCS:
90 oldest_key = min(last_books, key=operator.itemgetter('time'))
91 del last_books[oldest_key]
92 request.session['wiki_last_books'] = last_books
94 return direct_to_template(request, template_name, extra_context={
98 'REDMINE_URL': settings.REDMINE_URL,
102 def create_missing(request, slug):
103 slug = slug.replace(' ', '-')
105 if request.method == "POST":
106 form = DocumentCreateForm(request.POST, request.FILES)
109 if request.user.is_authenticated():
110 creator = request.user
113 book = Book.create(creator=creator,
114 slug=form.cleaned_data['slug'],
115 title=form.cleaned_data['title'],
116 text=form.cleaned_data['text'],
119 return http.HttpResponseRedirect(reverse("wiki_editor", args=[book.slug]))
121 form = DocumentCreateForm(initial={
123 "title": slug.replace('-', ' ').title(),
126 return direct_to_template(request, "wiki/document_create_missing.html", extra_context={
133 if request.method == "POST":
134 form = DocumentsUploadForm(request.POST, request.FILES)
138 if request.user.is_authenticated():
139 creator = request.user
143 zip = form.cleaned_data['zip']
148 existing = [book.slug for book in Book.objects.all()]
149 for filename in zip.namelist():
150 if filename[-1] == '/':
152 title = os.path.basename(filename)[:-4]
153 slug = slughifi(title)
154 if not (slug and filename.endswith('.xml')):
155 skipped_list.append(filename)
157 error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug])))
158 elif slug in existing:
159 error_list.append((filename, slug, _('Slug already used in repository.')))
162 zip.read(filename).decode('utf-8') # test read
163 ok_list.append((filename, slug, title))
164 except UnicodeDecodeError:
165 error_list.append((filename, title, _('File should be UTF-8 encoded.')))
166 slugs[slug] = filename
169 for filename, slug, title in ok_list:
170 Book.create(creator=creator,
173 text=zip.read(filename).decode('utf-8'),
176 return direct_to_template(request, "wiki/document_upload.html", extra_context={
179 "skipped_list": skipped_list,
180 "error_list": error_list,
183 form = DocumentsUploadForm()
185 return direct_to_template(request, "wiki/document_upload.html", extra_context={
191 @decorator_from_middleware(GZipMiddleware)
192 def text(request, slug):
193 doc = get_object_or_404(Book, slug=slug).doc
195 if request.method == 'POST':
196 form = DocumentTextSaveForm(request.POST, prefix="textsave")
199 # - stage completion should be stored (as a relation)
201 if request.user.is_authenticated():
202 author = request.user
205 text = form.cleaned_data['text']
206 parent_revision = form.cleaned_data['parent_revision']
207 parent = doc.at_revision(parent_revision)
208 doc.commit(author=author,
211 description=form.cleaned_data['comment'],
213 revision = doc.revision()
214 return JSONResponse({
215 'text': doc.materialize() if parent_revision != revision else None,
217 'revision': revision,
220 return JSONFormInvalid(form)
222 revision = request.GET.get("revision", None)
225 revision = int(revision)
229 return JSONResponse({
230 'text': doc.at_revision(revision).materialize(),
232 'revision': revision if revision else doc.revision(),
238 def revert(request, slug):
239 form = DocumentTextRevertForm(request.POST, prefix="textrevert")
241 doc = get_object_or_404(Book, slug=slug).doc
242 revision = form.cleaned_data['revision']
244 comment = form.cleaned_data['comment']
245 comment += "\n#revert to %s" % revision
247 if request.user.is_authenticated():
248 author = request.user
252 before = doc.revision()
253 logger.info("Reverting %s to %s", slug, revision)
254 doc.at_revision(revision).revert(author=author, description=comment)
256 return JSONResponse({
257 'text': doc.materialize() if before != doc.revision() else None,
259 'revision': doc.revision(),
262 return JSONFormInvalid(form)
266 def gallery(request, directory):
269 smart_unicode(settings.MEDIA_URL),
270 smart_unicode(settings.FILEBROWSER_DIRECTORY),
271 smart_unicode(directory)))
273 base_dir = os.path.join(
274 smart_unicode(settings.MEDIA_ROOT),
275 smart_unicode(settings.FILEBROWSER_DIRECTORY),
276 smart_unicode(directory))
278 def map_to_url(filename):
279 return "%s/%s" % (base_url, smart_unicode(filename))
281 def is_image(filename):
282 return os.path.splitext(f)[1].lower() in (u'.jpg', u'.jpeg', u'.png')
284 images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
286 return JSONResponse(images)
287 except (IndexError, OSError):
288 logger.exception("Unable to fetch gallery")
293 def diff(request, slug):
294 revA = int(request.GET.get('from', 0))
295 revB = int(request.GET.get('to', 0))
298 revA, revB = revB, revA
303 doc = get_object_or_404(Book, slug=slug).doc
304 docA = doc.at_revision(revA).materialize()
305 docB = doc.at_revision(revB).materialize()
307 return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
308 docB.splitlines(), context=3))
312 def revision(request, slug):
313 book = get_object_or_404(Book, slug=slug)
314 return http.HttpResponse(str(book.doc.revision()))
318 def history(request, slug):
320 book = get_object_or_404(Book, slug=slug)
321 rev = book.doc.revision()
323 for change in book.doc.history().order_by('-created_at'):
325 author = "%s %s <%s>" % (
326 change.author.first_name,
327 change.author.last_name,
333 "description": change.description,
335 "date": change.created_at,
339 return JSONResponse(changes)
348 @ajax_require_permission('wiki.can_change_tags')
349 def add_tag(request, name):
350 name = normalize_name(name)
351 storage = getstorage()
353 form = DocumentTagForm(request.POST, prefix="addtag")
355 doc = storage.get_or_404(form.cleaned_data['id'])
356 doc.add_tag(tag=form.cleaned_data['tag'],
357 revision=form.cleaned_data['revision'],
358 author=request.user.username)
359 return JSONResponse({"message": _("Tag added")})
361 return JSONFormInvalid(form)
365 @ajax_require_permission('wiki.can_publish')
366 def publish(request, name):
367 name = normalize_name(name)
369 storage = getstorage()
370 document = storage.get_by_tag(name, "ready_to_publish")
372 api = wlapi.WLAPI(**settings.WL_API_CONFIG)
375 return JSONResponse({"result": api.publish_book(document)})
376 except wlapi.APICallException, e:
377 return JSONServerError({"message": str(e)})
381 prefix = request.GET.get('q', '')
382 return http.HttpResponse('\n'.join([str(t) for t in Theme.objects.filter(name__istartswith=prefix)]))