2013-03-30 05:09:22 +11:00
|
|
|
import datetime
|
2013-06-06 03:40:41 +10:00
|
|
|
import difflib
|
|
|
|
import requests
|
2013-11-26 05:36:57 +11:00
|
|
|
import json
|
2013-03-30 05:09:22 +11:00
|
|
|
|
2013-11-25 05:18:56 +11:00
|
|
|
from django.shortcuts import (render_to_response, get_object_or_404)
|
2011-05-30 09:03:04 +10:00
|
|
|
from django.template.context import RequestContext
|
2013-06-06 03:40:41 +10:00
|
|
|
from django.http import (Http404, HttpResponseRedirect, HttpResponseBadRequest,
|
2013-11-25 05:18:56 +11:00
|
|
|
HttpResponse)
|
2011-05-30 09:03:04 +10:00
|
|
|
from django.conf import settings
|
2013-11-26 05:36:57 +11:00
|
|
|
from django.core.exceptions import ObjectDoesNotExist
|
2011-05-30 09:03:04 +10:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from django.core.urlresolvers import reverse
|
2013-03-23 05:33:11 +11:00
|
|
|
from django.db.models import Count
|
2013-06-06 03:40:41 +10:00
|
|
|
from django.views.defaults import (page_not_found as django_page_not_found,
|
2013-08-16 04:42:02 +10:00
|
|
|
server_error as django_server_error)
|
2013-12-18 08:52:21 +11:00
|
|
|
from django.views.decorators.csrf import csrf_exempt
|
2013-03-20 00:18:43 +11:00
|
|
|
|
2014-06-09 04:15:59 +10:00
|
|
|
from dpaste.forms import SnippetForm, get_expire_values, EXPIRE_CHOICES
|
2014-01-21 22:10:44 +11:00
|
|
|
from dpaste.models import Snippet, ONETIME_LIMIT
|
2013-08-16 04:42:02 +10:00
|
|
|
from dpaste.highlight import LEXER_WORDWRAP, LEXER_LIST
|
2013-11-25 05:18:56 +11:00
|
|
|
from dpaste.highlight import LEXER_DEFAULT, LEXER_KEYS
|
2012-04-15 04:11:49 +10:00
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Snippet Handling
|
|
|
|
# -----------------------------------------------------------------------------
|
2013-03-23 05:33:11 +11:00
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
def snippet_new(request, template_name='dpaste/snippet_new.html'):
|
2013-08-16 04:42:02 +10:00
|
|
|
"""
|
|
|
|
Create a new snippet.
|
|
|
|
"""
|
2011-05-30 09:03:04 +10:00
|
|
|
if request.method == "POST":
|
|
|
|
snippet_form = SnippetForm(data=request.POST, request=request)
|
|
|
|
if snippet_form.is_valid():
|
2013-08-16 04:42:02 +10:00
|
|
|
new_snippet = snippet_form.save()
|
2013-03-20 07:13:13 +11:00
|
|
|
url = new_snippet.get_absolute_url()
|
|
|
|
return HttpResponseRedirect(url)
|
2011-05-30 09:03:04 +10:00
|
|
|
else:
|
|
|
|
snippet_form = SnippetForm(request=request)
|
|
|
|
|
|
|
|
template_context = {
|
|
|
|
'snippet_form': snippet_form,
|
2013-08-16 04:42:02 +10:00
|
|
|
'lexer_list': LEXER_LIST,
|
2013-03-23 05:40:31 +11:00
|
|
|
'is_new': True,
|
2011-05-30 09:03:04 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
return render_to_response(
|
|
|
|
template_name,
|
|
|
|
template_context,
|
|
|
|
RequestContext(request)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def snippet_details(request, snippet_id, template_name='dpaste/snippet_details.html', is_raw=False):
|
2013-08-16 04:42:02 +10:00
|
|
|
"""
|
|
|
|
Details list view of a snippet. Handles the actual view, reply and
|
|
|
|
tree/diff view.
|
|
|
|
"""
|
2013-11-25 19:12:51 +11:00
|
|
|
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
|
2012-04-15 04:11:49 +10:00
|
|
|
|
2014-01-21 22:10:44 +11:00
|
|
|
# One time snippet get deleted if the view count matches our limit
|
2014-01-21 22:30:06 +11:00
|
|
|
if snippet.expire_type == Snippet.EXPIRE_ONETIME \
|
|
|
|
and snippet.view_count >= ONETIME_LIMIT:
|
2014-01-21 22:10:44 +11:00
|
|
|
snippet.delete()
|
|
|
|
raise Http404()
|
|
|
|
|
|
|
|
# Increase the view count of the snippet
|
|
|
|
snippet.view_count += 1
|
|
|
|
snippet.save()
|
|
|
|
|
2011-06-09 06:36:50 +10:00
|
|
|
tree = snippet.get_root()
|
|
|
|
tree = tree.get_descendants(include_self=True)
|
2011-05-30 09:03:04 +10:00
|
|
|
|
|
|
|
new_snippet_initial = {
|
|
|
|
'content': snippet.content,
|
|
|
|
'lexer': snippet.lexer,
|
|
|
|
}
|
|
|
|
|
|
|
|
if request.method == "POST":
|
2014-01-21 22:10:44 +11:00
|
|
|
snippet_form = SnippetForm(
|
|
|
|
data=request.POST,
|
|
|
|
request=request,
|
|
|
|
initial=new_snippet_initial)
|
2011-05-30 09:03:04 +10:00
|
|
|
if snippet_form.is_valid():
|
2013-08-16 04:42:02 +10:00
|
|
|
new_snippet = snippet_form.save(parent=snippet)
|
2013-03-20 07:13:13 +11:00
|
|
|
url = new_snippet.get_absolute_url()
|
|
|
|
return HttpResponseRedirect(url)
|
2011-05-30 09:03:04 +10:00
|
|
|
else:
|
2014-01-21 22:10:44 +11:00
|
|
|
snippet_form = SnippetForm(
|
|
|
|
initial=new_snippet_initial,
|
|
|
|
request=request)
|
2011-05-30 09:03:04 +10:00
|
|
|
|
|
|
|
template_context = {
|
|
|
|
'snippet_form': snippet_form,
|
|
|
|
'snippet': snippet,
|
2013-03-20 05:17:07 +11:00
|
|
|
'lexers': LEXER_LIST,
|
2011-05-30 09:03:04 +10:00
|
|
|
'lines': range(snippet.get_linecount()),
|
2011-06-09 06:36:50 +10:00
|
|
|
'tree': tree,
|
2012-04-15 04:11:49 +10:00
|
|
|
'wordwrap': snippet.lexer in LEXER_WORDWRAP and 'True' or 'False',
|
2011-05-30 09:03:04 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
response = render_to_response(
|
|
|
|
template_name,
|
|
|
|
template_context,
|
|
|
|
RequestContext(request)
|
|
|
|
)
|
|
|
|
|
|
|
|
if is_raw:
|
2011-09-09 11:01:37 +10:00
|
|
|
response['Content-Type'] = 'text/plain;charset=UTF-8'
|
2013-12-18 04:11:25 +11:00
|
|
|
response['X-Content-Type-Options'] = 'nosniff'
|
2011-05-30 09:03:04 +10:00
|
|
|
return response
|
|
|
|
else:
|
|
|
|
return response
|
|
|
|
|
2013-07-19 17:54:18 +10:00
|
|
|
|
2013-09-28 01:54:05 +10:00
|
|
|
def snippet_delete(request, snippet_id=None):
|
2013-07-19 17:54:18 +10:00
|
|
|
"""
|
2013-08-16 04:42:02 +10:00
|
|
|
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.
|
2013-07-19 17:54:18 +10:00
|
|
|
"""
|
2013-09-28 01:54:05 +10:00
|
|
|
snippet_id = snippet_id or request.POST.get('snippet_id')
|
|
|
|
if not snippet_id:
|
|
|
|
raise Http404('No snippet id given')
|
2011-05-30 09:03:04 +10:00
|
|
|
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
|
|
|
|
snippet.delete()
|
2013-11-25 04:16:29 +11:00
|
|
|
return HttpResponseRedirect(reverse('snippet_new'))
|
2011-05-30 09:03:04 +10:00
|
|
|
|
2011-06-08 20:23:08 +10:00
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
def snippet_history(request, template_name='dpaste/snippet_list.html'):
|
|
|
|
"""
|
|
|
|
Display the last `n` snippets created by this user (and saved in his
|
|
|
|
session).
|
|
|
|
"""
|
2013-11-25 04:16:29 +11:00
|
|
|
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)
|
|
|
|
|
|
|
|
if 'delete-all' in request.GET:
|
|
|
|
if snippet_list:
|
|
|
|
for s in snippet_list:
|
|
|
|
s.delete()
|
|
|
|
return HttpResponseRedirect(reverse('snippet_history'))
|
2011-06-08 20:23:08 +10:00
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
template_context = {
|
2013-08-16 04:42:02 +10:00
|
|
|
'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10),
|
2011-05-30 09:03:04 +10:00
|
|
|
'snippet_list': snippet_list,
|
|
|
|
}
|
|
|
|
|
|
|
|
return render_to_response(
|
|
|
|
template_name,
|
|
|
|
template_context,
|
|
|
|
RequestContext(request)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def snippet_diff(request, template_name='dpaste/snippet_diff.html'):
|
2013-08-16 04:42:02 +10:00
|
|
|
"""
|
|
|
|
Display a diff between two given snippet secret ids.
|
|
|
|
"""
|
2011-06-03 18:56:11 +10:00
|
|
|
if request.GET.get('a') and request.GET.get('a').isdigit() \
|
|
|
|
and request.GET.get('b') and request.GET.get('b').isdigit():
|
2011-05-30 09:03:04 +10:00
|
|
|
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.')
|
|
|
|
|
2013-03-20 05:17:07 +11:00
|
|
|
class DiffText(object):
|
|
|
|
pass
|
|
|
|
|
|
|
|
diff = DiffText()
|
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
if fileA.content != fileB.content:
|
|
|
|
d = difflib.unified_diff(
|
|
|
|
fileA.content.splitlines(),
|
|
|
|
fileB.content.splitlines(),
|
|
|
|
'Original',
|
|
|
|
'Current',
|
|
|
|
lineterm=''
|
|
|
|
)
|
2013-03-20 05:17:07 +11:00
|
|
|
|
|
|
|
diff.content = '\n'.join(d).strip()
|
|
|
|
diff.lexer = 'diff'
|
2011-05-30 09:03:04 +10:00
|
|
|
else:
|
2013-03-20 05:17:07 +11:00
|
|
|
diff.content = _(u'No changes were made between this two files.')
|
|
|
|
diff.lexer = 'text'
|
2011-05-30 09:03:04 +10:00
|
|
|
|
|
|
|
template_context = {
|
2013-03-20 05:17:07 +11:00
|
|
|
'snippet': diff,
|
2011-05-30 09:03:04 +10:00
|
|
|
'fileA': fileA,
|
|
|
|
'fileB': fileB,
|
|
|
|
}
|
|
|
|
|
|
|
|
return render_to_response(
|
|
|
|
template_name,
|
|
|
|
template_context,
|
|
|
|
RequestContext(request)
|
|
|
|
)
|
2011-06-08 20:23:08 +10:00
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
|
2013-11-26 05:36:57 +11:00
|
|
|
def snippet_gist(request, snippet_id): # pragma: no cover
|
2013-06-06 03:40:41 +10:00
|
|
|
"""
|
|
|
|
Put a snippet on Github Gist.
|
|
|
|
"""
|
|
|
|
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
|
|
|
|
data = {
|
2014-05-05 13:02:54 +10:00
|
|
|
'description': getattr(settings, 'DPASTE_DEFAULT_GIST_DESCRIPTION', 'the description for this gist'),
|
2013-06-06 03:40:41 +10:00
|
|
|
'public': False,
|
|
|
|
'files': {
|
2014-05-05 13:02:54 +10:00
|
|
|
getattr(settings, 'DPASTE_DEFAULT_GIST_NAME', 'dpaste.de_snippet.py'): {
|
2013-06-06 03:40:41 +10:00
|
|
|
'content': snippet.content,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try:
|
2013-11-26 05:36:57 +11:00
|
|
|
payload = json.dumps(data)
|
2013-06-06 03:40:41 +10:00
|
|
|
response = requests.post('https://api.github.com/gists', data=payload)
|
2014-05-05 12:52:37 +10:00
|
|
|
response_dict = response.json()
|
2013-06-06 03:40:41 +10:00
|
|
|
gist_url = response_dict.get('html_url')
|
|
|
|
|
|
|
|
# Github could be down, could return invalid JSON, it's rare
|
|
|
|
except:
|
|
|
|
return HttpResponse('Creating a Github Gist failed. Sorry, please go back and try again.')
|
|
|
|
|
|
|
|
return HttpResponseRedirect(gist_url)
|
|
|
|
|
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Static pages
|
|
|
|
# -----------------------------------------------------------------------------
|
2013-06-06 03:40:41 +10:00
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
def about(request, template_name='dpaste/about.html'):
|
|
|
|
"""
|
|
|
|
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],
|
|
|
|
}
|
|
|
|
|
|
|
|
return render_to_response(
|
|
|
|
template_name,
|
|
|
|
template_context,
|
|
|
|
RequestContext(request)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# API Handling
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
2013-11-25 05:18:56 +11:00
|
|
|
def _format_default(s):
|
|
|
|
"""The default response is the snippet URL wrapped in quotes."""
|
|
|
|
return u'"%s%s"' % (BASE_URL, s.get_absolute_url())
|
|
|
|
|
|
|
|
def _format_url(s):
|
|
|
|
"""The `url` format returns the snippet URL, no quotes, but a linebreak after."""
|
|
|
|
return u'%s%s\n' % (BASE_URL, s.get_absolute_url())
|
|
|
|
|
|
|
|
def _format_json(s):
|
|
|
|
"""The `json` format export."""
|
2013-11-26 05:36:57 +11:00
|
|
|
return json.dumps({
|
2013-11-25 05:18:56 +11:00
|
|
|
'url': u'%s%s' % (BASE_URL, s.get_absolute_url()),
|
|
|
|
'content': s.content,
|
|
|
|
'lexer': s.lexer,
|
|
|
|
})
|
|
|
|
|
2013-11-26 05:36:57 +11:00
|
|
|
BASE_URL = getattr(settings, 'DPASTE_BASE_URL', 'https://dpaste.de')
|
|
|
|
|
2013-11-25 05:18:56 +11:00
|
|
|
FORMAT_MAPPING = {
|
|
|
|
'default': _format_default,
|
|
|
|
'url': _format_url,
|
|
|
|
'json': _format_json,
|
|
|
|
}
|
2013-11-26 05:36:57 +11:00
|
|
|
|
2013-12-18 08:52:21 +11:00
|
|
|
@csrf_exempt
|
2013-11-25 05:18:56 +11:00
|
|
|
def snippet_api(request):
|
2013-08-16 04:42:02 +10:00
|
|
|
content = request.POST.get('content', '').strip()
|
2014-01-12 02:09:11 +11:00
|
|
|
lexer = request.REQUEST.get('lexer', LEXER_DEFAULT).strip()
|
2014-06-09 04:15:59 +10:00
|
|
|
expires = request.REQUEST.get('expires', '').strip()
|
2014-01-12 02:09:11 +11:00
|
|
|
format = request.REQUEST.get('format', 'default').strip()
|
2013-08-16 04:42:02 +10:00
|
|
|
|
|
|
|
if not content:
|
2013-11-25 05:18:56 +11:00
|
|
|
return HttpResponseBadRequest('No content given')
|
|
|
|
|
|
|
|
if not lexer in LEXER_KEYS:
|
|
|
|
return HttpResponseBadRequest('Invalid lexer given. Valid lexers are: %s' %
|
|
|
|
', '.join(LEXER_KEYS))
|
2013-08-16 04:42:02 +10:00
|
|
|
|
2014-06-09 04:15:59 +10:00
|
|
|
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
|
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
s = Snippet.objects.create(
|
|
|
|
content=content,
|
2013-11-25 05:18:56 +11:00
|
|
|
lexer=lexer,
|
2014-06-09 04:15:59 +10:00
|
|
|
expires=expires,
|
|
|
|
expire_type=expire_type,
|
2013-08-16 04:42:02 +10:00
|
|
|
)
|
|
|
|
s.save()
|
|
|
|
|
2013-11-25 05:18:56 +11:00
|
|
|
if not format in FORMAT_MAPPING:
|
|
|
|
response = _format_default(s)
|
|
|
|
else:
|
|
|
|
response = FORMAT_MAPPING[format](s)
|
|
|
|
|
2011-05-30 09:03:04 +10:00
|
|
|
return HttpResponse(response)
|
2013-03-20 00:18:43 +11:00
|
|
|
|
|
|
|
|
2013-08-16 04:42:02 +10:00
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
# Custom 404 and 500 views. Its easier to integrate this as a app if we
|
|
|
|
# handle them here.
|
|
|
|
# -----------------------------------------------------------------------------
|
|
|
|
|
2013-03-20 00:18:43 +11:00
|
|
|
def page_not_found(request, template_name='dpaste/404.html'):
|
2013-11-26 05:36:57 +11:00
|
|
|
return django_page_not_found(request, template_name) # pragma: no cover
|
2013-08-16 04:42:02 +10:00
|
|
|
|
2013-03-20 00:18:43 +11:00
|
|
|
def server_error(request, template_name='dpaste/500.html'):
|
2013-11-26 05:36:57 +11:00
|
|
|
return django_server_error(request, template_name) # pragma: no cover
|