from difflib import HtmlDiff from django.shortcuts import get_object_or_404, render_to_response, redirect from django.http import HttpResponseForbidden from django.views.decorators.http import require_POST from django.contrib.auth.decorators import login_required from django.template import RequestContext from django.utils.functional import wraps from markupwiki.models import Article, PUBLIC, PRIVATE, DELETED, LOCKED from markupwiki.forms import ArticleForm, StaffModerationForm, ModerationForm def title_check(view): def new_view(request, title, *args, **kwargs): newtitle = title.replace(' ', '_') if newtitle != title: return redirect(request.path.replace(title, newtitle)) else: return view(request, title, *args, **kwargs) return wraps(view)(new_view) @title_check def view_article(request, title, n=None): ''' view an article (or a specific revision of an article) if n is specified will show nth revision, otherwise latest will be shown if the article does not exist the user will be redirected to the edit page Context: article - ``Article`` instance version - ``ArticleVersion`` to display form - ``ModerationForm`` or ``StaffModerationForm`` instance only present if user is staff or the article creator Templates: article.html - default template used deleted_article.html - template used if article has been deleted private_article.html - template used if article is private for user ''' try: article = Article.objects.get(title=title) except Article.DoesNotExist: return redirect('edit_article', title) if n: version = article.versions.get(number=n) else: version = article.versions.latest() version.is_latest = True # set editable flag on article article.editable = article.is_editable_by_user(request.user) context = {'article':article, 'version': version} if request.user.is_staff: context['form'] = StaffModerationForm(instance=article) elif request.user == article.creator and article.status in (PUBLIC, PRIVATE): context['form'] = ModerationForm(instance=article) if article.is_deleted(): return render_to_response('markupwiki/deleted_article.html', context, context_instance=RequestContext(request)) elif (article.is_private() and request.user != article.creator and not request.user.is_staff): return render_to_response('private_article.html', context, context_instance=RequestContext(request)) return render_to_response('markupwiki/article.html', context, context_instance=RequestContext(request)) @title_check @login_required def edit_article(request, title): ''' edit (or create) an article Context: title - title of article being edited article - article being edited (potentially None) form - form to edit article Templates: edit_article.html - Default template for editing the article. locked_article.html - Template shown if editing is locked. ''' try: article = Article.objects.get(title=title) except Article.DoesNotExist: article = None if article and article.is_locked(): return render_to_response('locked_article.html', {'article': article}) if request.method == 'GET': # either get an empty ArticleForm or one based on latest version if article: version = article.versions.latest() form = ArticleForm(data={'body':version.body, 'body_markup_type':version.body_markup_type}) else: form = ArticleForm() elif request.method == 'POST': form = ArticleForm(request.POST) if form.is_valid(): if not article: # if article doesn't exist create it and start num at 0 article = Article.objects.create(title=title, creator=request.user) num = 0 else: # otherwise get latest num num = article.versions.latest().number + 1 # create a new version attached to article specified in name version = form.save(False) version.article = article version.author = request.user version.number = num version.save() # redirect to view article on save return redirect(article) return render_to_response('edit_article.html', {'title':title, 'article':article, 'form': form}) @require_POST @title_check def article_status(request, title): ''' POST-only view to update article status ''' article = get_object_or_404(Article, title=title) status = int(request.POST['status']) # can only change status to/from locked or deleted if staff if article.status in (LOCKED, DELETED) or status in (LOCKED, DELETED): perm_test = lambda u,a: u.is_staff # can only change status to/from public/private if staff or creator elif article.status in (PUBLIC, PRIVATE) or status in (PUBLIC, PRIVATE): perm_test = lambda u,a: u.is_staff or u == a.creator # check that requrired permissions are met before updating status if perm_test(request.user, article): article.status = status article.save() return redirect(article) else: return HttpResponseForbidden('access denied') @title_check def article_history(request, title): article = get_object_or_404(Article, title=title) versions = article.versions.filter(removed=False) return render_to_response('history.html', {'article':article, 'versions':versions}) @title_check def article_diff(request, title): article = get_object_or_404(Article, title=title) from_id = int(request.GET['from']) to_id = int(request.GET['to']) from_version = article.versions.get(number=from_id) to_version = article.versions.get(number=to_id) differ = HtmlDiff() from_body = from_version.body.raw.split('\n') to_body = to_version.body.raw.split('\n') table = differ.make_table(from_body, to_body) return render_to_response('article_diff.html', {'table':table})