Make all views class-based.

This commit is contained in:
Martin Mahner 2016-03-24 20:14:13 +01:00
parent 82d49d0ed1
commit 1c2e26c930
4 changed files with 228 additions and 207 deletions

View file

@ -2,34 +2,34 @@
<form method="post" action="" class="form-horizontal">
{% csrf_token %}
{{ snippet_form.non_field_errors }}
<div style="display: none;">{{ snippet_form.title }}</div>
{{ form.non_field_errors }}
<div style="display: none;">{{ form.title }}</div>
<div class="
control-group
form-content
superenter
{% if is_new %}autofocus{% endif %}
{% if snippet_form.content.errors %}error{% endif %}
{% if not object %}autofocus{% endif %}
{% if form.content.errors %}error{% endif %}
">
{{ snippet_form.content }}
{{ form.content }}
</div>
<div class="control-group form-options">
<div class="form-options-lexer
{% if snippet_form.lexer.errors %}control-group error{% endif %}">
{% if form.lexer.errors %}control-group error{% endif %}">
<div class="input-append">
{{ snippet_form.lexer }}
{{ form.lexer }}
</div>
{% for error in snippet_form.lexer.errors %}
{% for error in form.lexer.errors %}
<span class="help-inline">{{ error }}</span>
{% endfor %}
</div>
<div class="form-options-expire">
{{ snippet_form.expires.errors }}
{{ form.expires.errors }}
<div class="input-prepend">
<span class="add-on"><i class="icon-trash" title="{% trans "Expire in" %}"></i></span>
{{ snippet_form.expires }}
{{ form.expires }}
</div>
</div>
</div>

View file

@ -6,14 +6,15 @@ from .. import views
L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4)
urlpatterns = [
url(r'^about/$', views.about, name='dpaste_about'),
url(r'^about/$', views.AboutView.as_view(), name='dpaste_about'),
url(r'^$', views.snippet_new, name='snippet_new'),
url(r'^diff/$', views.snippet_diff, name='snippet_diff'),
url(r'^history/$', views.snippet_history, name='snippet_history'),
url(r'^delete/$', views.snippet_delete, name='snippet_delete'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$' % L, views.snippet_details, name='snippet_details'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/delete/$' % L, views.snippet_delete, name='snippet_delete'),
url(r'^$', views.SnippetView.as_view(), name='snippet_new'),
url(r'^diff/$', views.SnippetDiffView.as_view(), name='snippet_diff'),
url(r'^history/$', views.SnippetHistory.as_view(), name='snippet_history'),
url(r'^delete/$', views.SnippetDeleteView.as_view(), name='snippet_delete'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/?$' % L, views.SnippetDetailView.as_view(), name='snippet_details'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/delete/$' % L, views.SnippetDeleteView.as_view(), name='snippet_delete'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/gist/$' % L, views.snippet_gist, name='snippet_gist'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L, views.snippet_details, {'template_name': 'dpaste/snippet_details_raw.html', 'is_raw': True}, name='snippet_details_raw'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L, views.SnippetRawView.as_view(), name='snippet_details_raw'),
]

View file

@ -1,7 +1,7 @@
from django.conf.urls import patterns, url
from ..views import snippet_api
from ..views import APIView
urlpatterns = [
url(r'^api/$', snippet_api, name='dpaste_api_create_snippet'),
url(r'^api/$', APIView.as_view(), name='dpaste_api_create_snippet'),
]

View file

@ -9,12 +9,14 @@ from django.core.urlresolvers import reverse
from django.db.models import Count
from django.http import (Http404, HttpResponse, HttpResponseBadRequest,
HttpResponseRedirect)
from django.shortcuts import get_object_or_404, render_to_response
from django.template.context import RequestContext
from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.defaults import page_not_found as django_page_not_found
from django.views.defaults import server_error as django_server_error
from django.views.generic.base import View, TemplateView
from django.views.generic.detail import DetailView
from pygments.lexers import get_lexer_for_filename
from pygments.util import ClassNotFound
@ -33,183 +35,196 @@ template_globals = {
# Snippet Handling
# -----------------------------------------------------------------------------
def snippet_new(request, template_name='dpaste/snippet_new.html'):
from django.views.generic import FormView
class SnippetView(FormView):
"""
Create a new snippet.
"""
if request.method == "POST":
snippet_form = SnippetForm(data=request.POST, request=request)
if snippet_form.is_valid():
new_snippet = snippet_form.save()
url = new_snippet.get_absolute_url()
return HttpResponseRedirect(url)
else:
snippet_form = SnippetForm(request=request)
form_class = SnippetForm
template_name = 'dpaste/snippet_new.html'
template_context = {
'snippet_form': snippet_form,
'lexer_list': LEXER_LIST,
'is_new': True,
}
def get_form_kwargs(self):
kwargs = super(SnippetView, self).get_form_kwargs()
kwargs.update({
'request': self.request,
})
return kwargs
return render_to_response(
template_name,
template_context,
RequestContext(request, template_globals)
)
def get_context_data(self, **kwargs):
ctx = super(SnippetView, self).get_context_data(**kwargs)
ctx.update(template_globals)
ctx.update({
'lexer_list': LEXER_LIST,
})
return ctx
def form_valid(self, form):
snippet = form.save()
return HttpResponseRedirect(snippet.get_absolute_url())
def snippet_details(request, snippet_id, template_name='dpaste/snippet_details.html', is_raw=False):
class SnippetDetailView(SnippetView, DetailView):
"""
Details list view of a snippet. Handles the actual view, reply and
tree/diff view.
"""
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
queryset = Snippet.objects.all()
template_name = 'dpaste/snippet_details.html'
slug_url_kwarg = 'snippet_id'
slug_field = 'secret_id'
# One-Time snippet get deleted if the view count matches our limit
if snippet.expire_type == Snippet.EXPIRE_ONETIME \
and snippet.view_count >= ONETIME_LIMIT:
snippet.delete()
raise Http404()
def get(self, *args, **kwargs):
snippet = self.get_object()
# Increase the view count of the snippet
snippet.view_count += 1
snippet.save()
# One-Time snippet get deleted if the view count matches our limit
if snippet.expire_type == Snippet.EXPIRE_ONETIME \
and snippet.view_count >= ONETIME_LIMIT:
snippet.delete()
raise Http404()
tree = snippet.get_root()
tree = tree.get_descendants(include_self=True)
# Increase the view count of the snippet
snippet.view_count += 1
snippet.save()
new_snippet_initial = {
'content': snippet.content,
'lexer': snippet.lexer,
}
return super(SnippetDetailView, self).get(*args, **kwargs)
if request.method == "POST":
snippet_form = SnippetForm(
data=request.POST,
request=request,
initial=new_snippet_initial)
if snippet_form.is_valid():
new_snippet = snippet_form.save(parent=snippet)
url = new_snippet.get_absolute_url()
return HttpResponseRedirect(url)
else:
snippet_form = SnippetForm(
initial=new_snippet_initial,
request=request)
def get_initial(self):
snippet = self.get_object()
return {
'content': snippet.content,
'lexer': snippet.lexer,
}
template_context = {
'snippet_form': snippet_form,
'snippet': snippet,
'lexers': LEXER_LIST,
'lines': range(snippet.get_linecount()),
'tree': tree,
'wordwrap': snippet.lexer in LEXER_WORDWRAP and 'True' or 'False',
'gist': getattr(settings, 'DPASTE_ENABLE_GIST', True),
}
def form_valid(self, form):
snippet = form.save(parent=self.get_object())
return HttpResponseRedirect(snippet.get_absolute_url())
response = render_to_response(
template_name,
template_context,
RequestContext(request, template_globals)
)
def get_context_data(self, **kwargs):
self.object = snippet = self.get_object()
tree = snippet.get_root().get_descendants(include_self=True)
if is_raw:
ctx = super(SnippetDetailView, self).get_context_data(**kwargs)
ctx.update(template_globals)
ctx.update({
'lines': range(snippet.get_linecount()),
'tree': tree,
'wordwrap': snippet.lexer in LEXER_WORDWRAP and 'True' or 'False',
'gist': getattr(settings, 'DPASTE_ENABLE_GIST', True),
})
return ctx
class SnippetRawView(SnippetDetailView):
"""
Display the raw content of a snippet
"""
template_name = 'dpaste/snippet_details_raw.html'
def render_to_response(self, context, **response_kwargs):
snippet = self.get_object()
response = HttpResponse(snippet.content)
response['Content-Type'] = 'text/plain;charset=UTF-8'
response['X-Content-Type-Options'] = 'nosniff'
return response
else:
return response
def snippet_delete(request, snippet_id=None):
class SnippetDeleteView(View):
"""
Delete a snippet. This is allowed by anybody as long as he knows the
snippet id. I got too many manual requests to do this, mostly for legal
reasons and the chance to abuse this is not given anyway, since snippets
always expire.
"""
snippet_id = snippet_id or request.POST.get('snippet_id')
if not snippet_id:
raise Http404('No snippet id given')
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
snippet.delete()
return HttpResponseRedirect(reverse('snippet_new'))
def dispatch(self, request, *args, **kwargs):
snippet_id = self.kwargs.get('snippet_id') or request.POST.get('snippet_id')
if not snippet_id:
raise Http404('No snippet id given')
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
snippet.delete()
return HttpResponseRedirect(reverse('snippet_new'))
def snippet_history(request, template_name='dpaste/snippet_list.html'):
class SnippetHistory(TemplateView):
"""
Display the last `n` snippets created by this user (and saved in his
session).
"""
snippet_list = None
snippet_id_list = request.session.get('snippet_list', None)
if snippet_id_list:
snippet_list = Snippet.objects.filter(pk__in=snippet_id_list)
template_name = 'dpaste/snippet_list.html'
if 'delete-all' in request.GET:
if snippet_list:
for s in snippet_list:
s.delete()
return HttpResponseRedirect(reverse('snippet_history'))
def get(self, request, *args, **kwargs):
snippet_id_list = request.session.get('snippet_list', [])
self.snippet_list = Snippet.objects.filter(pk__in=snippet_id_list)
template_context = {
'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10),
'snippet_list': snippet_list,
}
if 'delete-all' in request.GET:
self.snippet_list.delete()
return HttpResponseRedirect(reverse('snippet_history'))
return super(SnippetHistory, self).get(request, *args, **kwargs)
return render_to_response(
template_name,
template_context,
RequestContext(request, template_globals)
)
def get_context_data(self, **kwargs):
ctx = super(SnippetHistory, self).get_context_data(**kwargs)
ctx.update(template_globals)
ctx.update({
'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10),
'snippet_list': self.snippet_list,
})
return ctx
def snippet_diff(request, template_name='dpaste/snippet_diff.html'):
class SnippetDiffView(TemplateView):
"""
Display a diff between two given snippet secret ids.
"""
if request.GET.get('a') and request.GET.get('a').isdigit() \
and request.GET.get('b') and request.GET.get('b').isdigit():
try:
fileA = Snippet.objects.get(pk=int(request.GET.get('a')))
fileB = Snippet.objects.get(pk=int(request.GET.get('b')))
except ObjectDoesNotExist:
return HttpResponseBadRequest(u'Selected file(s) does not exist.')
else:
return HttpResponseBadRequest(u'You must select two snippets.')
template_name = 'dpaste/snippet_diff.html'
class DiffText(object):
pass
def get(self, request, *args, **kwargs):
"""
Some validation around input files we will compare later.
"""
if request.GET.get('a') and request.GET.get('a').isdigit() \
and request.GET.get('b') and request.GET.get('b').isdigit():
try:
self.fileA = Snippet.objects.get(pk=int(request.GET.get('a')))
self.fileB = Snippet.objects.get(pk=int(request.GET.get('b')))
except ObjectDoesNotExist:
return HttpResponseBadRequest(u'Selected file(s) does not exist.')
else:
return HttpResponseBadRequest(u'You must select two snippets.')
diff = DiffText()
return super(SnippetDiffView, self).get(request, *args, **kwargs)
if fileA.content != fileB.content:
d = difflib.unified_diff(
fileA.content.splitlines(),
fileB.content.splitlines(),
'Original',
'Current',
lineterm=''
)
def get_diff(self):
class DiffText(object):
pass
diff.content = '\n'.join(d).strip()
diff.lexer = 'diff'
else:
diff.content = _(u'No changes were made between this two files.')
diff.lexer = 'text'
diff = DiffText()
template_context = {
'snippet': diff,
'fileA': fileA,
'fileB': fileB,
}
if self.fileA.content != self.fileB.content:
d = difflib.unified_diff(
self.fileA.content.splitlines(),
self.fileB.content.splitlines(),
'Original',
'Current',
lineterm=''
)
return render_to_response(
template_name,
template_context,
RequestContext(request, template_globals)
)
diff.content = '\n'.join(d).strip()
diff.lexer = 'diff'
else:
diff.content = _(u'No changes were made between this two files.')
diff.lexer = 'text'
return diff
def get_context_data(self, **kwargs):
ctx = super(SnippetDiffView, self).get_context_data(**kwargs)
ctx.update(template_globals)
ctx.update({
'snippet': self.get_diff(),
'fileA': self.fileA,
'fileB': self.fileB,
})
return ctx
def snippet_gist(request, snippet_id): # pragma: no cover
@ -247,22 +262,22 @@ def snippet_gist(request, snippet_id): # pragma: no cover
# Static pages
# -----------------------------------------------------------------------------
def about(request, template_name='dpaste/about.html'):
class AboutView(TemplateView):
"""
A rather static page, we need a view just to display a couple of
statistics.
"""
template_context = {
'total': Snippet.objects.count(),
'stats': Snippet.objects.values('lexer').annotate(
count=Count('lexer')).order_by('-count')[:5],
}
template_name = 'dpaste/about.html'
return render_to_response(
template_name,
template_context,
RequestContext(request, template_globals)
)
def get_context_data(self, **kwargs):
ctx = super(AboutView, self).get_context_data(**kwargs)
ctx.update(template_globals)
ctx.update({
'total': Snippet.objects.count(),
'stats': Snippet.objects.values('lexer').annotate(
count=Count('lexer')).order_by('-count')[:5],
})
return ctx
# -----------------------------------------------------------------------------
@ -293,61 +308,66 @@ FORMAT_MAPPING = {
'json': _format_json,
}
@csrf_exempt
def snippet_api(request):
content = request.POST.get('content', '').strip()
lexer = request.POST.get('lexer', LEXER_DEFAULT).strip()
filename = request.POST.get('filename', '').strip()
expires = request.POST.get('expires', '').strip()
format = request.POST.get('format', 'default').strip()
if not content:
return HttpResponseBadRequest('No content given')
class APIView(View):
"""
API View
"""
@method_decorator(csrf_exempt)
def post(self, request, *args, **kwargs):
content = request.POST.get('content', '').strip()
lexer = request.POST.get('lexer', LEXER_DEFAULT).strip()
filename = request.POST.get('filename', '').strip()
expires = request.POST.get('expires', '').strip()
format = request.POST.get('format', 'default').strip()
# We need at least a lexer or a filename
if not lexer and not filename:
return HttpResponseBadRequest('No lexer or filename given. Unable to '
'determine a highlight. Valid lexers are: %s' % ', '.join(LEXER_KEYS))
if not content:
return HttpResponseBadRequest('No content given')
# A lexer is given, check if its valid at all
if lexer and lexer not in LEXER_KEYS:
return HttpResponseBadRequest('Invalid lexer "%s" given. Valid lexers are: %s' % (
lexer, ', '.join(LEXER_KEYS)))
# We need at least a lexer or a filename
if not lexer and not filename:
return HttpResponseBadRequest('No lexer or filename given. Unable to '
'determine a highlight. Valid lexers are: %s' % ', '.join(LEXER_KEYS))
# No lexer is given, but we have a filename, try to get the lexer out of it.
# In case Pygments cannot determine the lexer of the filename, we fallback
# to 'plain' code.
if not lexer and filename:
try:
lexer_cls = get_lexer_for_filename(filename)
lexer = lexer_cls.aliases[0]
except (ClassNotFound, IndexError):
lexer = PLAIN_CODE
# A lexer is given, check if its valid at all
if lexer and lexer not in LEXER_KEYS:
return HttpResponseBadRequest('Invalid lexer "%s" given. Valid lexers are: %s' % (
lexer, ', '.join(LEXER_KEYS)))
if expires:
expire_options = [str(i) for i in dict(EXPIRE_CHOICES).keys()]
if not expires in expire_options:
return HttpResponseBadRequest('Invalid expire choice "{}" given. '
'Valid values are: {}'.format(expires, ', '.join(expire_options)))
expires, expire_type = get_expire_values(expires)
else:
expires = datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24 * 30)
expire_type = Snippet.EXPIRE_TIME
# No lexer is given, but we have a filename, try to get the lexer out of it.
# In case Pygments cannot determine the lexer of the filename, we fallback
# to 'plain' code.
if not lexer and filename:
try:
lexer_cls = get_lexer_for_filename(filename)
lexer = lexer_cls.aliases[0]
except (ClassNotFound, IndexError):
lexer = PLAIN_CODE
s = Snippet.objects.create(
content=content,
lexer=lexer,
expires=expires,
expire_type=expire_type,
)
s.save()
if expires:
expire_options = [str(i) for i in dict(EXPIRE_CHOICES).keys()]
if not expires in expire_options:
return HttpResponseBadRequest('Invalid expire choice "{}" given. '
'Valid values are: {}'.format(expires, ', '.join(expire_options)))
expires, expire_type = get_expire_values(expires)
else:
expires = datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24 * 30)
expire_type = Snippet.EXPIRE_TIME
if not format in FORMAT_MAPPING:
response = _format_default(s)
else:
response = FORMAT_MAPPING[format](s)
s = Snippet.objects.create(
content=content,
lexer=lexer,
expires=expires,
expire_type=expire_type,
)
s.save()
return HttpResponse(response)
if not format in FORMAT_MAPPING:
response = _format_default(s)
else:
response = FORMAT_MAPPING[format](s)
return HttpResponse(response)
# -----------------------------------------------------------------------------