2 from StringIO import StringIO
 
   4 logger = logging.getLogger("fnp.wiki")
 
   8 from django.conf import settings
 
  10 from django.views.generic.simple import direct_to_template
 
  11 from django.views.decorators.http import require_POST, require_GET
 
  12 from django.core.urlresolvers import reverse
 
  13 from wiki.helpers import (JSONResponse, JSONFormInvalid, JSONServerError,
 
  14                 ajax_require_permission, recursive_groupby)
 
  15 from django import http
 
  16 from django.shortcuts import get_object_or_404, redirect
 
  17 from django.http import Http404
 
  19 from wiki.models import Book, Chunk, Theme
 
  20 from wiki.forms import DocumentTextSaveForm, DocumentTextRevertForm, DocumentTagForm, DocumentCreateForm, DocumentsUploadForm
 
  21 from datetime import datetime
 
  22 from django.utils.encoding import smart_unicode
 
  23 from django.utils.translation import ugettext_lazy as _
 
  24 from django.utils.decorators import decorator_from_middleware
 
  25 from django.middleware.gzip import GZipMiddleware
 
  31 # Quick hack around caching problems, TODO: use ETags
 
  33 from django.views.decorators.cache import never_cache
 
  42 def document_list(request):
 
  43     return direct_to_template(request, 'wiki/document_list.html', extra_context={
 
  44         'books': Book.objects.all(),
 
  45         'last_books': sorted(request.session.get("wiki_last_books", {}).items(),
 
  46                         key=lambda x: x[1]['time'], reverse=True),
 
  51 def editor(request, slug, chunk=None, template_name='wiki/document_details.html'):
 
  53         chunk = Chunk.get(slug, chunk)
 
  54     except Chunk.MultipleObjectsReturned:
 
  57     except Chunk.DoesNotExist:
 
  60                 book = Book.objects.get(slug=slug)
 
  61             except Book.DoesNotExist:
 
  62                 return http.HttpResponseRedirect(reverse("wiki_create_missing", args=[slug]))
 
  66     access_time = datetime.now()
 
  67     last_books = request.session.get("wiki_last_books", {})
 
  68     last_books[slug, chunk.slug] = {
 
  70         'title': chunk.pretty_name(),
 
  73     if len(last_books) > MAX_LAST_DOCS:
 
  74         oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
 
  75         del last_books[oldest_key]
 
  76     request.session['wiki_last_books'] = last_books
 
  78     return direct_to_template(request, template_name, extra_context={
 
  81             "text_save": DocumentTextSaveForm(prefix="textsave"),
 
  82             "text_revert": DocumentTextRevertForm(prefix="textrevert"),
 
  83             "add_tag": DocumentTagForm(prefix="addtag"),
 
  85         'REDMINE_URL': settings.REDMINE_URL,
 
  90 def editor_readonly(request, slug, chunk=None, template_name='wiki/document_details_readonly.html'):
 
  92         chunk = Chunk.get(slug, chunk)
 
  93         revision = request.GET['revision']
 
  94     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist, KeyError):
 
  97     access_time = datetime.now()
 
  98     last_books = request.session.get("wiki_last_books", {})
 
  99     last_books[slug, chunk.slug] = {
 
 101         'title': chunk.book.title,
 
 104     if len(last_books) > MAX_LAST_DOCS:
 
 105         oldest_key = min(last_books, key=lambda x: last_books[x]['time'])
 
 106         del last_books[oldest_key]
 
 107     request.session['wiki_last_books'] = last_books
 
 109     return direct_to_template(request, template_name, extra_context={
 
 111         'revision': revision,
 
 113         'REDMINE_URL': settings.REDMINE_URL,
 
 117 def create_missing(request, slug):
 
 118     slug = slug.replace(' ', '-')
 
 120     if request.method == "POST":
 
 121         form = DocumentCreateForm(request.POST, request.FILES)
 
 124             if request.user.is_authenticated():
 
 125                 creator = request.user
 
 128             book = Book.create(creator=creator,
 
 129                 slug=form.cleaned_data['slug'],
 
 130                 title=form.cleaned_data['title'],
 
 131                 text=form.cleaned_data['text'],
 
 134             return http.HttpResponseRedirect(reverse("wiki_editor", args=[book.slug]))
 
 136         form = DocumentCreateForm(initial={
 
 138                 "title": slug.replace('-', ' ').title(),
 
 141     return direct_to_template(request, "wiki/document_create_missing.html", extra_context={
 
 148     if request.method == "POST":
 
 149         form = DocumentsUploadForm(request.POST, request.FILES)
 
 153             if request.user.is_authenticated():
 
 154                 creator = request.user
 
 158             zip = form.cleaned_data['zip']
 
 163             existing = [book.slug for book in Book.objects.all()]
 
 164             for filename in zip.namelist():
 
 165                 if filename[-1] == '/':
 
 167                 title = os.path.basename(filename)[:-4]
 
 168                 slug = slughifi(title)
 
 169                 if not (slug and filename.endswith('.xml')):
 
 170                     skipped_list.append(filename)
 
 172                     error_list.append((filename, slug, _('Slug already used for %s' % slugs[slug])))
 
 173                 elif slug in existing:
 
 174                     error_list.append((filename, slug, _('Slug already used in repository.')))
 
 177                         zip.read(filename).decode('utf-8') # test read
 
 178                         ok_list.append((filename, slug, title))
 
 179                     except UnicodeDecodeError:
 
 180                         error_list.append((filename, title, _('File should be UTF-8 encoded.')))
 
 181                     slugs[slug] = filename
 
 184                 for filename, slug, title in ok_list:
 
 185                     Book.create(creator=creator,
 
 188                         text=zip.read(filename).decode('utf-8'),
 
 191             return direct_to_template(request, "wiki/document_upload.html", extra_context={
 
 194                 "skipped_list": skipped_list,
 
 195                 "error_list": error_list,
 
 198         form = DocumentsUploadForm()
 
 200     return direct_to_template(request, "wiki/document_upload.html", extra_context={
 
 206 @decorator_from_middleware(GZipMiddleware)
 
 207 def text(request, slug, chunk=None):
 
 209         doc = Chunk.get(slug, chunk)
 
 210     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
 
 213     if request.method == 'POST':
 
 214         form = DocumentTextSaveForm(request.POST, prefix="textsave")
 
 217             # - stage completion should be stored (as a relation)
 
 219             if request.user.is_authenticated():
 
 220                 author = request.user
 
 223             text = form.cleaned_data['text']
 
 224             parent_revision = form.cleaned_data['parent_revision']
 
 225             parent = doc.at_revision(parent_revision)
 
 226             doc.commit(author=author,
 
 229                        description=form.cleaned_data['comment'],
 
 231             revision = doc.revision()
 
 232             return JSONResponse({
 
 233                 'text': doc.materialize() if parent_revision != revision else None,
 
 235                 'revision': revision,
 
 238             return JSONFormInvalid(form)
 
 240         revision = request.GET.get("revision", None)
 
 243             revision = int(revision)
 
 244         except (ValueError, TypeError):
 
 247         return JSONResponse({
 
 248             'text': doc.at_revision(revision).materialize(),
 
 250             'revision': revision if revision else doc.revision(),
 
 255 def book_xml(request, slug):
 
 256     xml = get_object_or_404(Book, slug=slug).materialize()
 
 258     response = http.HttpResponse(xml, content_type='application/xml', mimetype='application/wl+xml')
 
 259     response['Content-Disposition'] = 'attachment; filename=%s.xml' % slug
 
 264 def book_txt(request, slug):
 
 265     xml = get_object_or_404(Book, slug=slug).materialize()
 
 268     librarian.text.transform(StringIO(xml), output)
 
 269     text = output.getvalue()
 
 270     response = http.HttpResponse(text, content_type='text/plain', mimetype='text/plain')
 
 271     response['Content-Disposition'] = 'attachment; filename=%s.txt' % slug
 
 276 def book_html(request, slug):
 
 277     xml = get_object_or_404(Book, slug=slug).materialize()
 
 280     librarian.html.transform(StringIO(xml), output, parse_dublincore=False,
 
 282     html = output.getvalue()
 
 283     response = http.HttpResponse(html, content_type='text/html', mimetype='text/html')
 
 289 def revert(request, slug, chunk=None):
 
 290     form = DocumentTextRevertForm(request.POST, prefix="textrevert")
 
 293             doc = Chunk.get(slug, chunk)
 
 294         except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
 
 297         revision = form.cleaned_data['revision']
 
 299         comment = form.cleaned_data['comment']
 
 300         comment += "\n#revert to %s" % revision
 
 302         if request.user.is_authenticated():
 
 303             author = request.user
 
 307         before = doc.revision()
 
 308         logger.info("Reverting %s to %s", slug, revision)
 
 309         doc.at_revision(revision).revert(author=author, description=comment)
 
 311         return JSONResponse({
 
 312             'text': doc.materialize() if before != doc.revision() else None,
 
 314             'revision': doc.revision(),
 
 317         return JSONFormInvalid(form)
 
 321 def gallery(request, directory):
 
 324                         smart_unicode(settings.MEDIA_URL),
 
 325                         smart_unicode(settings.FILEBROWSER_DIRECTORY),
 
 326                         smart_unicode(directory)))
 
 328         base_dir = os.path.join(
 
 329                     smart_unicode(settings.MEDIA_ROOT),
 
 330                     smart_unicode(settings.FILEBROWSER_DIRECTORY),
 
 331                     smart_unicode(directory))
 
 333         def map_to_url(filename):
 
 334             return "%s/%s" % (base_url, smart_unicode(filename))
 
 336         def is_image(filename):
 
 337             return os.path.splitext(f)[1].lower() in (u'.jpg', u'.jpeg', u'.png')
 
 339         images = [map_to_url(f) for f in map(smart_unicode, os.listdir(base_dir)) if is_image(f)]
 
 341         return JSONResponse(images)
 
 342     except (IndexError, OSError):
 
 343         logger.exception("Unable to fetch gallery")
 
 348 def diff(request, slug, chunk=None):
 
 349     revA = int(request.GET.get('from', 0))
 
 350     revB = int(request.GET.get('to', 0))
 
 353         revA, revB = revB, revA
 
 359         doc = Chunk.get(slug, chunk)
 
 360     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
 
 362     docA = doc.at_revision(revA).materialize()
 
 363     docB = doc.at_revision(revB).materialize()
 
 365     return http.HttpResponse(nice_diff.html_diff_table(docA.splitlines(),
 
 366                                          docB.splitlines(), context=3))
 
 370 def revision(request, slug, chunk=None):
 
 372         doc = Chunk.get(slug, chunk)
 
 373     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
 
 375     return http.HttpResponse(str(doc.revision()))
 
 379 def history(request, slug, chunk=None):
 
 382         doc = Chunk.get(slug, chunk)
 
 383     except (Chunk.MultipleObjectsReturned, Chunk.DoesNotExist):
 
 387     for change in doc.history().order_by('-created_at'):
 
 389                 "version": change.revision,
 
 390                 "description": change.description,
 
 391                 "author": change.author_str(),
 
 392                 "date": change.created_at,
 
 395     return JSONResponse(changes)
 
 398 def book(request, slug):
 
 399     book = get_object_or_404(Book, slug=slug)
 
 401     return direct_to_template(request, "wiki/book_detail.html", extra_context={
 
 412 @ajax_require_permission('wiki.can_change_tags')
 
 413 def add_tag(request, name):
 
 414     name = normalize_name(name)
 
 415     storage = getstorage()
 
 417     form = DocumentTagForm(request.POST, prefix="addtag")
 
 419         doc = storage.get_or_404(form.cleaned_data['id'])
 
 420         doc.add_tag(tag=form.cleaned_data['tag'],
 
 421                     revision=form.cleaned_data['revision'],
 
 422                     author=request.user.username)
 
 423         return JSONResponse({"message": _("Tag added")})
 
 425         return JSONFormInvalid(form)
 
 429 @ajax_require_permission('wiki.can_publish')
 
 430 def publish(request, name):
 
 431     name = normalize_name(name)
 
 433     storage = getstorage()
 
 434     document = storage.get_by_tag(name, "ready_to_publish")
 
 436     api = wlapi.WLAPI(**settings.WL_API_CONFIG)
 
 439         return JSONResponse({"result": api.publish_book(document)})
 
 440     except wlapi.APICallException, e:
 
 441         return JSONServerError({"message": str(e)})
 
 445     prefix = request.GET.get('q', '')
 
 446     return http.HttpResponse('\n'.join([str(t) for t in Theme.objects.filter(name__istartswith=prefix)]))