434 lines
15 KiB
Python
434 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
import types
|
|
from django.core.urlresolvers import get_callable
|
|
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseServerError, HttpResponseForbidden, HttpResponseNotAllowed
|
|
from django.utils import simplejson
|
|
from djangomako.shortcuts import render_to_response, render_to_string
|
|
from django.shortcuts import get_object_or_404
|
|
from django.template import RequestContext, Context, loader
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from django.core.urlresolvers import reverse
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.db.models import Q
|
|
from django.conf import settings
|
|
from django.shortcuts import redirect
|
|
from django.core.context_processors import csrf
|
|
|
|
from django.template import Context
|
|
from django.http import HttpResponse
|
|
|
|
import djangomako.middleware
|
|
from mako.template import Template
|
|
from mako.lookup import TemplateLookup
|
|
|
|
from models import *
|
|
from settings import *
|
|
|
|
def view(request, wiki_url):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
perm_err = check_permissions(request, article, check_read=True)
|
|
if perm_err:
|
|
return perm_err
|
|
d = {'wiki_article': article,
|
|
'wiki_write': article.can_write_l(request.user),
|
|
'wiki_attachments_write': article.can_attach(request.user),
|
|
}
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_view.html', d)
|
|
|
|
def root_redirect(request):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
try:
|
|
root = Article.get_root()
|
|
except:
|
|
err = not_found(request, '/')
|
|
return err
|
|
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(root.get_url())))
|
|
|
|
def create(request, wiki_url):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
url_path = get_url_path(wiki_url)
|
|
|
|
if url_path != [] and url_path[0].startswith('_'):
|
|
d = {'wiki_err_keyword': True,
|
|
'wiki_url': '/'.join(url_path) }
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_error.html', d)
|
|
|
|
# Lookup path
|
|
try:
|
|
# Ensure that the path exists...
|
|
root = Article.get_root()
|
|
# Remove root slug if present in path
|
|
if url_path and root.slug == url_path[0]:
|
|
url_path = url_path[1:]
|
|
|
|
path = Article.get_url_reverse(url_path[:-1], root)
|
|
if not path:
|
|
d = {'wiki_err_noparent': True,
|
|
'wiki_url_parent': '/'.join(url_path[:-1]) }
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_error.html', d)
|
|
|
|
perm_err = check_permissions(request, path[-1], check_locked=False, check_write=True)
|
|
if perm_err:
|
|
return perm_err
|
|
# Ensure doesn't already exist
|
|
article = Article.get_url_reverse(url_path, root)
|
|
if article:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article[-1].get_url(),)))
|
|
|
|
# TODO: Somehow this doesnt work...
|
|
#except ShouldHaveExactlyOneRootSlug, (e):
|
|
except:
|
|
if Article.objects.filter(parent=None).count() > 0:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=('/',)))
|
|
# Root not found...
|
|
path = []
|
|
url_path = [""]
|
|
|
|
if request.method == 'POST':
|
|
f = CreateArticleForm(request.POST)
|
|
if f.is_valid():
|
|
article = Article()
|
|
article.slug = url_path[-1]
|
|
if not request.user.is_anonymous():
|
|
article.created_by = request.user
|
|
article.title = f.cleaned_data.get('title')
|
|
if path != []:
|
|
article.parent = path[-1]
|
|
a = article.save()
|
|
new_revision = f.save(commit=False)
|
|
if not request.user.is_anonymous():
|
|
new_revision.revision_user = request.user
|
|
new_revision.article = article
|
|
new_revision.save()
|
|
import django.db as db
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
else:
|
|
f = CreateArticleForm(initial={'title':request.GET.get('wiki_article_name', url_path[-1]),
|
|
'contents':_('Headline\n===\n\n')})
|
|
|
|
d = {'wiki_form': f,
|
|
'wiki_write': True,
|
|
}
|
|
d.update(csrf(request))
|
|
|
|
return render_to_response('simplewiki_create.html', d)
|
|
|
|
def edit(request, wiki_url):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
# Check write permissions
|
|
perm_err = check_permissions(request, article, check_write=True, check_locked=True)
|
|
if perm_err:
|
|
return perm_err
|
|
|
|
if WIKI_ALLOW_TITLE_EDIT:
|
|
EditForm = RevisionFormWithTitle
|
|
else:
|
|
EditForm = RevisionForm
|
|
|
|
if request.method == 'POST':
|
|
f = EditForm(request.POST)
|
|
if f.is_valid():
|
|
new_revision = f.save(commit=False)
|
|
new_revision.article = article
|
|
# Check that something has actually been changed...
|
|
if not new_revision.get_diff():
|
|
return (None, HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),))))
|
|
if not request.user.is_anonymous():
|
|
new_revision.revision_user = request.user
|
|
new_revision.save()
|
|
if WIKI_ALLOW_TITLE_EDIT:
|
|
new_revision.article.title = f.cleaned_data['title']
|
|
new_revision.article.save()
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
else:
|
|
f = EditForm({'contents': article.current_revision.contents, 'title': article.title})
|
|
d = {'wiki_form': f,
|
|
'wiki_write': True,
|
|
'wiki_article': article,
|
|
'wiki_attachments_write': article.can_attach(request.user),
|
|
}
|
|
d.update(csrf(request))
|
|
|
|
return render_to_response('simplewiki_edit.html', d)
|
|
|
|
def history(request, wiki_url, page=1):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
perm_err = check_permissions(request, article, check_read=True)
|
|
if perm_err:
|
|
return perm_err
|
|
|
|
page_size = 10
|
|
|
|
try:
|
|
p = int(page)
|
|
except ValueError:
|
|
p = 1
|
|
|
|
history = Revision.objects.filter(article__exact = article).order_by('-counter')
|
|
|
|
if request.method == 'POST':
|
|
if request.POST.__contains__('revision'):
|
|
perm_err = check_permissions(request, article, check_write=True, check_locked=True)
|
|
if perm_err:
|
|
return perm_err
|
|
try:
|
|
r = int(request.POST['revision'])
|
|
article.current_revision = Revision.objects.get(id=r)
|
|
article.save()
|
|
except:
|
|
pass
|
|
finally:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
|
|
page_count = (history.count()+(page_size-1)) / page_size
|
|
if p > page_count:
|
|
p = 1
|
|
beginItem = (p-1) * page_size
|
|
|
|
next_page = p + 1 if page_count > p else None
|
|
prev_page = p - 1 if p > 1 else None
|
|
|
|
d = {'wiki_page': p,
|
|
'wiki_next_page': next_page,
|
|
'wiki_prev_page': prev_page,
|
|
'wiki_write': article.can_write_l(request.user),
|
|
'wiki_attachments_write': article.can_attach(request.user),
|
|
'wiki_article': article,
|
|
'wiki_history': history[beginItem:beginItem+page_size],}
|
|
d.update(csrf(request))
|
|
|
|
return render_to_response('simplewiki_history.html', d)
|
|
|
|
def search_articles(request):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
# blampe: We should check for the presence of other popular django search
|
|
# apps and use those if possible. Only fall back on this as a last resort.
|
|
# Adding some context to results (eg where matches were) would also be nice.
|
|
|
|
# todo: maybe do some perm checking here
|
|
|
|
if request.method == 'POST':
|
|
querystring = request.POST['value'].strip()
|
|
else:
|
|
querystring = ""
|
|
|
|
if querystring:
|
|
results = Article.objects.all()
|
|
for queryword in querystring.split():
|
|
# Basic negation is as fancy as we get right now
|
|
if queryword[0] == '-' and len(queryword) > 1:
|
|
results._search = lambda x: results.exclude(x)
|
|
queryword = queryword[1:]
|
|
else:
|
|
results._search = lambda x: results.filter(x)
|
|
|
|
results = results._search(Q(current_revision__contents__icontains = queryword) | \
|
|
Q(title__icontains = queryword))
|
|
else:
|
|
# Need to throttle results by splitting them into pages...
|
|
results = Article.objects.all()
|
|
|
|
if results.count() == 1 and querystring:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(results[0].get_url(),)))
|
|
else:
|
|
d = {'wiki_search_results': results,
|
|
'wiki_search_query': querystring}
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_searchresults.html', d)
|
|
|
|
|
|
def search_add_related(request, wiki_url):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
perm_err = check_permissions(request, article, check_read=True)
|
|
if perm_err:
|
|
return perm_err
|
|
|
|
search_string = request.GET.get('query', None)
|
|
self_pk = request.GET.get('self', None)
|
|
if search_string:
|
|
results = []
|
|
related = Article.objects.filter(title__istartswith = search_string)
|
|
others = article.related.all()
|
|
if self_pk:
|
|
related = related.exclude(pk=self_pk)
|
|
if others:
|
|
related = related.exclude(related__in = others)
|
|
related = related.order_by('title')[:10]
|
|
for item in related:
|
|
results.append({'id': str(item.id),
|
|
'value': item.title,
|
|
'info': item.get_url()})
|
|
else:
|
|
results = []
|
|
|
|
json = simplejson.dumps({'results': results})
|
|
return HttpResponse(json, mimetype='application/json')
|
|
|
|
def add_related(request, wiki_url):
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
perm_err = check_permissions(request, article, check_write=True, check_locked=True)
|
|
if perm_err:
|
|
return perm_err
|
|
|
|
try:
|
|
related_id = request.POST['id']
|
|
rel = Article.objects.get(id=related_id)
|
|
has_already = article.related.filter(id=related_id).count()
|
|
if has_already == 0 and not rel == article:
|
|
article.related.add(rel)
|
|
article.save()
|
|
except:
|
|
pass
|
|
finally:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
|
|
def remove_related(request, wiki_url, related_id):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
|
|
(article, path, err) = fetch_from_url(request, wiki_url)
|
|
if err:
|
|
return err
|
|
|
|
perm_err = check_permissions(request, article, check_write=True, check_locked=True)
|
|
if perm_err:
|
|
return perm_err
|
|
|
|
try:
|
|
rel_id = int(related_id)
|
|
rel = Article.objects.get(id=rel_id)
|
|
article.related.remove(rel)
|
|
article.save()
|
|
except:
|
|
pass
|
|
finally:
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
|
|
def random_article(request):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
from random import randint
|
|
num_arts = Article.objects.count()
|
|
article = Article.objects.all()[randint(0, num_arts-1)]
|
|
return HttpResponseRedirect(reverse('wiki_view', args=(article.get_url(),)))
|
|
|
|
def encode_err(request, url):
|
|
d = {'wiki_err_encode': True}
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_error.html', d)
|
|
|
|
def not_found(request, wiki_url):
|
|
if not request.user.is_authenticated():
|
|
return redirect('/')
|
|
"""Generate a NOT FOUND message for some URL"""
|
|
d = {'wiki_err_notfound': True,
|
|
'wiki_url': wiki_url}
|
|
d.update(csrf(request))
|
|
return render_to_response('simplewiki_error.html', d)
|
|
|
|
def get_url_path(url):
|
|
"""Return a list of all actual elements of a url, safely ignoring
|
|
double-slashes (//) """
|
|
return filter(lambda x: x!='', url.split('/'))
|
|
|
|
def fetch_from_url(request, url):
|
|
"""Analyze URL, returning the article and the articles in its path
|
|
If something goes wrong, return an error HTTP response"""
|
|
|
|
err = None
|
|
article = None
|
|
path = None
|
|
|
|
url_path = get_url_path(url)
|
|
|
|
try:
|
|
root = Article.get_root()
|
|
except:
|
|
err = not_found(request, '/')
|
|
return (article, path, err)
|
|
|
|
if url_path and root.slug == url_path[0]:
|
|
url_path = url_path[1:]
|
|
|
|
path = Article.get_url_reverse(url_path, root)
|
|
if not path:
|
|
err = not_found(request, '/' + '/'.join(url_path))
|
|
else:
|
|
article = path[-1]
|
|
return (article, path, err)
|
|
|
|
|
|
def check_permissions(request, article, check_read=False, check_write=False, check_locked=False):
|
|
read_err = check_read and not article.can_read(request.user)
|
|
write_err = check_write and not article.can_write(request.user)
|
|
locked_err = check_locked and article.locked
|
|
|
|
if read_err or write_err or locked_err:
|
|
d = {'wiki_article': article,
|
|
'wiki_err_noread': read_err,
|
|
'wiki_err_nowrite': write_err,
|
|
'wiki_err_locked': locked_err,}
|
|
d.update(csrf(request))
|
|
# TODO: Make this a little less jarring by just displaying an error
|
|
# on the current page? (no such redirect happens for an anon upload yet)
|
|
# benjaoming: I think this is the nicest way of displaying an error, but
|
|
# these errors shouldn't occur, but rather be prevented on the other pages.
|
|
return render_to_response('simplewiki_error.html', d)
|
|
else:
|
|
return None
|
|
|
|
####################
|
|
# LOGIN PROTECTION #
|
|
####################
|
|
|
|
if WIKI_REQUIRE_LOGIN_VIEW:
|
|
view = login_required(view)
|
|
history = login_required(history)
|
|
# search_related = login_required(search_related)
|
|
# wiki_encode_err = login_required(wiki_encode_err)
|
|
|
|
if WIKI_REQUIRE_LOGIN_EDIT:
|
|
create = login_required(create)
|
|
edit = login_required(edit)
|
|
add_related = login_required(add_related)
|
|
remove_related = login_required(remove_related)
|
|
|
|
if WIKI_CONTEXT_PREPROCESSORS:
|
|
settings.TEMPLATE_CONTEXT_PROCESSORS = settings.TEMPLATE_CONTEXT_PROCESSORS + WIKI_CONTEXT_PREPROCESSORS
|