importing old work
This commit is contained in:
commit
b12fc0b42f
0
markupwiki/__init__.py
Normal file
0
markupwiki/__init__.py
Normal file
19
markupwiki/forms.py
Normal file
19
markupwiki/forms.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
from django import forms
|
||||||
|
from markupwiki.models import Article, ArticleVersion, PUBLIC, PRIVATE
|
||||||
|
|
||||||
|
class ArticleForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = ArticleVersion
|
||||||
|
fields = ['body', 'body_markup_type']
|
||||||
|
|
||||||
|
class StaffModerationForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
fields = ['status']
|
||||||
|
|
||||||
|
class ModerationForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Article
|
||||||
|
fields = ['status']
|
||||||
|
status = forms.ChoiceField(choices=((PUBLIC, 'Public'),
|
||||||
|
(PRIVATE, 'Private')))
|
41
markupwiki/models.py
Normal file
41
markupwiki/models.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
from markupfield.fields import MarkupField
|
||||||
|
|
||||||
|
PUBLIC, PRIVATE, LOCKED, DELETED = range(4)
|
||||||
|
ARTICLE_STATUSES = (
|
||||||
|
(PUBLIC, 'Public'), # public - no restrictions on viewing/editing
|
||||||
|
(PRIVATE, 'Private'), # private - only creator / admins can view
|
||||||
|
(LOCKED, 'Locked'), # locked - only admins can edit
|
||||||
|
(DELETED, 'Deleted'), # deleted - display deleted page
|
||||||
|
)
|
||||||
|
|
||||||
|
class Article(models.Model):
|
||||||
|
title = models.CharField(max_length=50)
|
||||||
|
creator = models.ForeignKey(User, related_name='wiki_articles')
|
||||||
|
status = models.IntegerField(choices=ARTICLE_STATUSES, default=PUBLIC)
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('view_article', args=[self.title])
|
||||||
|
|
||||||
|
class ArticleVersion(models.Model):
|
||||||
|
article = models.ForeignKey(Article, related_name='versions')
|
||||||
|
author = models.ForeignKey(User, related_name='article_versions')
|
||||||
|
number = models.PositiveIntegerField()
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
removed = models.BooleanField(default=False)
|
||||||
|
body = MarkupField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['timestamp']
|
||||||
|
get_latest_by = 'timestamp'
|
||||||
|
|
||||||
|
def __unicode__(self):
|
||||||
|
return unicode(self.article)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse('article_version', args=[self.article.title, self.number])
|
23
markupwiki/templates/article.html
Normal file
23
markupwiki/templates/article.html
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<h2>{{article.title}}</h2>
|
||||||
|
|
||||||
|
{% if article.locked %} <a href="{% url edit_article article.title %}">edit article</a> {% endif %}
|
||||||
|
| <a href="{% url article_history article.title %}">view history</a>
|
||||||
|
|
||||||
|
{% if form %}
|
||||||
|
<form method="POST" action="{% url update_article_status article.title %}">
|
||||||
|
{{ form.as_ul }}
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<div class="about">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
{% if article.deleted %}
|
||||||
|
This article has been deleted.
|
||||||
|
{% else %}
|
||||||
|
{{version.body}}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
2
markupwiki/templates/article_diff.html
Normal file
2
markupwiki/templates/article_diff.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
{{ table|safe }}
|
13
markupwiki/templates/deleted_article.html
Normal file
13
markupwiki/templates/deleted_article.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<h2>Article "{{article.title}}" has been deleted</h2>
|
||||||
|
|
||||||
|
<a href="{% url edit_article article.title %}">edit article</a>
|
||||||
|
|
||||||
|
{% if form %}
|
||||||
|
<form method="POST" action="{% url update_article_status article.title %}">
|
||||||
|
{{ form.as_ul }}
|
||||||
|
<input type="submit" />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
<p>Once an article has been deleted only a moderator can create an article with the same name.</p>
|
10
markupwiki/templates/edit_article.html
Normal file
10
markupwiki/templates/edit_article.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{% if article %}
|
||||||
|
<h2>Editing Article "{{title}}"</h2>
|
||||||
|
{% else %}
|
||||||
|
<h2>Creating Article "{{title}}"</h2>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="POST" action=".">
|
||||||
|
{{ form.as_ul }}
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
32
markupwiki/templates/history.html
Normal file
32
markupwiki/templates/history.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
<h2>History {{article.title}}</h2>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>Version</th>
|
||||||
|
<th>Author</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Compare From</th>
|
||||||
|
<th>Compare To</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<form action="{% url article_diff article.title %}" method="GET">
|
||||||
|
{% for version in versions %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{{version.get_absolute_url}}">
|
||||||
|
{% ifequal version.number 0 %}
|
||||||
|
Initial Version
|
||||||
|
{% else %}
|
||||||
|
Revision {{version.number}}
|
||||||
|
{% endifequal %}
|
||||||
|
</a></td>
|
||||||
|
<td>{{version.author}}</td>
|
||||||
|
<td><a href="{{version.get_absolute_url}}">{{version.timestamp}}</a></td>
|
||||||
|
<td><input type="radio" name="from" value="{{version.number}}"></td>
|
||||||
|
<td><input type="radio" name="to" value="{{version.number}}"></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
5
markupwiki/templates/private_article.html
Normal file
5
markupwiki/templates/private_article.html
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<h2>This Article is Private</h2>
|
||||||
|
|
||||||
|
<div class="body">
|
||||||
|
Only the author of this article may view it.
|
||||||
|
</div>
|
13
markupwiki/urls.py
Normal file
13
markupwiki/urls.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from django.conf.urls.defaults import *
|
||||||
|
|
||||||
|
WIKI_REGEX = r'^(?P<title>[\w\s]+)'
|
||||||
|
|
||||||
|
urlpatterns = patterns('markupwiki.views',
|
||||||
|
url(WIKI_REGEX + '/$', 'view_article', name='view_article'),
|
||||||
|
url(WIKI_REGEX + '/edit/$', 'edit_article', name='edit_article'),
|
||||||
|
url(WIKI_REGEX + '/update_status/$', 'article_status',
|
||||||
|
name='update_article_status'),
|
||||||
|
url(WIKI_REGEX + '/history/$', 'article_history', name='article_history'),
|
||||||
|
url(WIKI_REGEX + '/history/(?P<n>\d+)/$', 'view_article', name='article_version'),
|
||||||
|
url(WIKI_REGEX + '/diff/$', 'article_diff', name='article_diff'),
|
||||||
|
)
|
125
markupwiki/views.py
Normal file
125
markupwiki/views.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
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):
|
||||||
|
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()
|
||||||
|
|
||||||
|
context = {'article':article, 'version': version}
|
||||||
|
|
||||||
|
if request.user.is_staff:
|
||||||
|
context['form'] = StaffModerationForm(instance=article)
|
||||||
|
elif request.user == article.creator:
|
||||||
|
context['form'] = ModerationForm(instance=article)
|
||||||
|
|
||||||
|
if article.status == DELETED:
|
||||||
|
return render_to_response('deleted_article.html', context,
|
||||||
|
context_instance=RequestContext(request))
|
||||||
|
elif (article.status == 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('article.html', context,
|
||||||
|
context_instance=RequestContext(request))
|
||||||
|
|
||||||
|
@title_check
|
||||||
|
@login_required
|
||||||
|
def edit_article(request, title):
|
||||||
|
try:
|
||||||
|
article = Article.objects.get(title=title)
|
||||||
|
except Article.DoesNotExist:
|
||||||
|
article = None
|
||||||
|
|
||||||
|
if article.locked:
|
||||||
|
return render_to_response('article_locked.html', {'article': article})
|
||||||
|
|
||||||
|
if request.method == 'GET':
|
||||||
|
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:
|
||||||
|
article = Article.objects.create(title=title,
|
||||||
|
creator=request.user)
|
||||||
|
num = 0
|
||||||
|
else:
|
||||||
|
num = article.versions.latest().number + 1
|
||||||
|
version = form.save(False)
|
||||||
|
version.article = article
|
||||||
|
version.author = request.user
|
||||||
|
version.number = num
|
||||||
|
version.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):
|
||||||
|
article = get_object_or_404(Article, title=title)
|
||||||
|
status = int(request.POST['status'])
|
||||||
|
|
||||||
|
if article.status in (LOCKED, DELETED) or status in (LOCKED, DELETED):
|
||||||
|
perm_test = lambda u,a: u.is_staff
|
||||||
|
elif article.status in (PUBLIC, PRIVATE) or status in (PUBLIC, PRIVATE):
|
||||||
|
perm_test = lambda u,a: u.is_staff or u == a.creator
|
||||||
|
|
||||||
|
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})
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Django
|
||||||
|
-e git+git@github.com:jamesturk/django-markupfield.git#egg=django-markupfield
|
Loading…
Reference in New Issue
Block a user