Added a API value to set the expire time of snippets. Closes issue #64.

This commit is contained in:
Martin Mahner 2014-06-08 20:15:59 +02:00
parent afafd4fb3b
commit 34181e03f7
5 changed files with 113 additions and 33 deletions

View file

@ -15,18 +15,18 @@ Available POST data for an API call:
``content`` (required) ``content`` (required)
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
Required. The UTF-8 encoded string you want to paste. The UTF-8 encoded string you want to paste.
``lexer`` (optional) ``lexer`` (optional)
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
Optional. Can also be set via GET. The lexer string key used for highlighting. Can also be set via GET. The lexer string key used for highlighting.
See `lexer list`_ for a full list of choices. Default: ``python``. See `lexer list`_ for a full list of choices. Default: ``python``.
``format`` (optional) ``format`` (optional)
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
Optional. Can also be set via GET. The format of the API response. Choices are: Can also be set via GET. The format of the API response. Choices are:
* ``default`` — Returns a full qualified URL wrapped in quotes. Example:: * ``default`` — Returns a full qualified URL wrapped in quotes. Example::
@ -47,6 +47,21 @@ Optional. Can also be set via GET. The format of the API response. Choices are:
"conent": "The text body of the snippet." "conent": "The text body of the snippet."
} }
``expires`` (optional)
~~~~~~~~~~~~~~~~~~~~~~
Can also be set via GET. A keyword to indicate the lifetime of a
snippetn in seconds. The values are predefined by the server. Calling this with
an invalid value returns a HTTP 400 BadRequest together with a list of valid
values. Default: ``2592000``. In the default configuration valid values are:
* onetime
* never
* 3600
* 604800
* 2592000
.. hint:: You need to adjust the setting ``DPASTE_BASE_URL`` which is used .. hint:: You need to adjust the setting ``DPASTE_BASE_URL`` which is used
to generate the full qualified URL in the API response. See :doc:`settings`. to generate the full qualified URL in the API response. See :doc:`settings`.

View file

@ -17,6 +17,20 @@ EXPIRE_CHOICES = getattr(settings, 'DPASTE_EXPIRE_CHOICES', (
EXPIRE_DEFAULT = getattr(settings, 'DPASTE_EXPIRE_DEFAULT', EXPIRE_CHOICES[3][0]) EXPIRE_DEFAULT = getattr(settings, 'DPASTE_EXPIRE_DEFAULT', EXPIRE_CHOICES[3][0])
MAX_CONTENT_LENGTH = getattr(settings, 'DPASTE_MAX_CONTENT_LENGTH', 250*1024*1024) MAX_CONTENT_LENGTH = getattr(settings, 'DPASTE_MAX_CONTENT_LENGTH', 250*1024*1024)
def get_expire_values(expires):
if expires == u'never':
expire_type = Snippet.EXPIRE_KEEP
expires = None
elif expires == u'onetime':
expire_type = Snippet.EXPIRE_ONETIME
expires = None
else:
expire_type = Snippet.EXPIRE_TIME
expires = expires and expires or EXPIRE_DEFAULT
expires = datetime.datetime.now() + datetime.timedelta(seconds=int(expires))
return expires, expire_type
class SnippetForm(forms.ModelForm): class SnippetForm(forms.ModelForm):
content = forms.CharField( content = forms.CharField(
label=_('Content'), label=_('Content'),
@ -71,43 +85,35 @@ class SnippetForm(forms.ModelForm):
raise forms.ValidationError(_('Plesae fill out this field.')) raise forms.ValidationError(_('Plesae fill out this field.'))
return content return content
def clean_expires(self):
"""
Extract the 'expire_type' from the choice of expire choices.
"""
expires = self.cleaned_data['expires']
expires, expire_type = get_expire_values(expires)
self.cleaned_data['expire_type'] = expire_type
return expires
def clean(self): def clean(self):
# The `title` field is a hidden honeypot field. If its filled, """
# this is likely spam. The `title` field is a hidden honeypot field. If its filled,
this is likely spam.
"""
if self.cleaned_data.get('title'): if self.cleaned_data.get('title'):
raise forms.ValidationError('This snippet was identified as Spam.') raise forms.ValidationError('This snippet was identified as Spam.')
return self.cleaned_data return self.cleaned_data
def clean_expires(self):
expires = self.cleaned_data['expires']
if expires == u'never':
self.cleaned_data['expire_type'] = Snippet.EXPIRE_KEEP
return None
if expires == u'onetime':
self.cleaned_data['expire_type'] = Snippet.EXPIRE_ONETIME
return None
self.cleaned_data['expire_type'] = Snippet.EXPIRE_TIME
return expires
def save(self, parent=None, *args, **kwargs): def save(self, parent=None, *args, **kwargs):
MAX_SNIPPETS_PER_USER = getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10) MAX_SNIPPETS_PER_USER = getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10)
# Set parent snippet # Set parent snippet
if parent:
self.instance.parent = parent self.instance.parent = parent
# Add expire datestamp. None indicates 'keep forever', use the default # Add expire datestamp. None indicates 'keep forever', use the default
# null state of the db column for that. # null state of the db column for that.
self.instance.expires = self.cleaned_data['expires']
self.instance.expire_type = self.cleaned_data['expire_type'] self.instance.expire_type = self.cleaned_data['expire_type']
expires = self.cleaned_data['expires']
if expires:
self.instance.expires = datetime.datetime.now() + \
datetime.timedelta(seconds=int(expires))
# Save snippet in the db # Save snippet in the db
super(SnippetForm, self).save(*args, **kwargs) super(SnippetForm, self).save(*args, **kwargs)

View file

@ -3,6 +3,7 @@
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test.client import Client from django.test.client import Client
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings
from ..models import Snippet from ..models import Snippet
@ -133,3 +134,53 @@ class SnippetAPITestCase(TestCase):
response = self.client.post(self.api_url, data) response = self.client.post(self.api_url, data)
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0) self.assertEqual(Snippet.objects.count(), 0)
def test_expire_choices_none_given(self):
# No expire choice given will set a default expiration of one month
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye"})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1)
self.assertTrue(Snippet.objects.all()[0].expires)
def test_expire_choices_invalid_given(self):
# A expire choice that does not exist returns a BadRequest
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'foobar'})
self.assertEqual(response.status_code, 400)
self.assertEqual(Snippet.objects.count(), 0)
def test_valid_expiration_choices(self):
"""
Test all the different expiration choices. We dont actually test
the deletion, since thats handled in the `test_snippet` section.
"""
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'onetime'})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1)
self.assertEqual(Snippet.objects.all()[0].expire_type, Snippet.EXPIRE_ONETIME)
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 'never'})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 2)
self.assertEqual(Snippet.objects.all()[0].expire_type, Snippet.EXPIRE_KEEP)
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 3)
self.assertTrue(Snippet.objects.all()[0].expires)
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 7})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 4)
self.assertTrue(Snippet.objects.all()[0].expires)
response = self.client.post(self.api_url, {
'content': u"Hello Wörld.\n\tGood Bye", 'expires': 3600 * 24 * 30})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 5)
self.assertTrue(Snippet.objects.all()[0].expires)

View file

@ -340,14 +340,10 @@ class SnippetTestCase(TestCase):
Snippets without an expiration date wont get deleted automatically. Snippets without an expiration date wont get deleted automatically.
""" """
data = self.valid_form_data() data = self.valid_form_data()
data['expires'] = 'never'
self.client.post(self.new_url, data, follow=True) self.client.post(self.new_url, data, follow=True)
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)
s = Snippet.objects.all()[0]
s.expires = None
s.save()
management.call_command('cleanup_snippets') management.call_command('cleanup_snippets')
self.assertEqual(Snippet.objects.count(), 1) self.assertEqual(Snippet.objects.count(), 1)

View file

@ -16,7 +16,7 @@ from django.views.defaults import (page_not_found as django_page_not_found,
server_error as django_server_error) server_error as django_server_error)
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from dpaste.forms import SnippetForm from dpaste.forms import SnippetForm, get_expire_values, EXPIRE_CHOICES
from dpaste.models import Snippet, ONETIME_LIMIT from dpaste.models import Snippet, ONETIME_LIMIT
from dpaste.highlight import LEXER_WORDWRAP, LEXER_LIST from dpaste.highlight import LEXER_WORDWRAP, LEXER_LIST
from dpaste.highlight import LEXER_DEFAULT, LEXER_KEYS from dpaste.highlight import LEXER_DEFAULT, LEXER_KEYS
@ -285,6 +285,7 @@ FORMAT_MAPPING = {
def snippet_api(request): def snippet_api(request):
content = request.POST.get('content', '').strip() content = request.POST.get('content', '').strip()
lexer = request.REQUEST.get('lexer', LEXER_DEFAULT).strip() lexer = request.REQUEST.get('lexer', LEXER_DEFAULT).strip()
expires = request.REQUEST.get('expires', '').strip()
format = request.REQUEST.get('format', 'default').strip() format = request.REQUEST.get('format', 'default').strip()
if not content: if not content:
@ -294,10 +295,21 @@ def snippet_api(request):
return HttpResponseBadRequest('Invalid lexer given. Valid lexers are: %s' % return HttpResponseBadRequest('Invalid lexer given. Valid lexers are: %s' %
', '.join(LEXER_KEYS)) ', '.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
s = Snippet.objects.create( s = Snippet.objects.create(
content=content, content=content,
lexer=lexer, lexer=lexer,
expires=datetime.datetime.now()+datetime.timedelta(seconds=60*60*24*30) expires=expires,
expire_type=expire_type,
) )
s.save() s.save()