mirror of
https://github.com/DarrenOfficial/dpaste.git
synced 2024-11-15 08:02:54 +11:00
Added a API value to set the expire time of snippets. Closes issue #64.
This commit is contained in:
parent
afafd4fb3b
commit
34181e03f7
5 changed files with 113 additions and 33 deletions
21
docs/api.rst
21
docs/api.rst
|
@ -15,18 +15,18 @@ Available POST data for an API call:
|
|||
``content`` (required)
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Required. The UTF-8 encoded string you want to paste.
|
||||
The UTF-8 encoded string you want to paste.
|
||||
|
||||
``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``.
|
||||
|
||||
``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::
|
||||
|
||||
|
@ -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."
|
||||
}
|
||||
|
||||
|
||||
``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
|
||||
to generate the full qualified URL in the API response. See :doc:`settings`.
|
||||
|
||||
|
|
|
@ -17,6 +17,20 @@ EXPIRE_CHOICES = getattr(settings, 'DPASTE_EXPIRE_CHOICES', (
|
|||
EXPIRE_DEFAULT = getattr(settings, 'DPASTE_EXPIRE_DEFAULT', EXPIRE_CHOICES[3][0])
|
||||
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):
|
||||
content = forms.CharField(
|
||||
label=_('Content'),
|
||||
|
@ -71,43 +85,35 @@ class SnippetForm(forms.ModelForm):
|
|||
raise forms.ValidationError(_('Plesae fill out this field.'))
|
||||
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):
|
||||
# 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'):
|
||||
raise forms.ValidationError('This snippet was identified as Spam.')
|
||||
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):
|
||||
MAX_SNIPPETS_PER_USER = getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10)
|
||||
|
||||
# Set parent snippet
|
||||
if parent:
|
||||
self.instance.parent = parent
|
||||
self.instance.parent = parent
|
||||
|
||||
# Add expire datestamp. None indicates 'keep forever', use the default
|
||||
# null state of the db column for that.
|
||||
self.instance.expires = self.cleaned_data['expires']
|
||||
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
|
||||
super(SnippetForm, self).save(*args, **kwargs)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
from django.core.urlresolvers import reverse
|
||||
from django.test.client import Client
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from ..models import Snippet
|
||||
|
||||
|
@ -133,3 +134,53 @@ class SnippetAPITestCase(TestCase):
|
|||
response = self.client.post(self.api_url, data)
|
||||
self.assertEqual(response.status_code, 400)
|
||||
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)
|
||||
|
|
|
@ -340,14 +340,10 @@ class SnippetTestCase(TestCase):
|
|||
Snippets without an expiration date wont get deleted automatically.
|
||||
"""
|
||||
data = self.valid_form_data()
|
||||
data['expires'] = 'never'
|
||||
self.client.post(self.new_url, data, follow=True)
|
||||
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
|
||||
s = Snippet.objects.all()[0]
|
||||
s.expires = None
|
||||
s.save()
|
||||
|
||||
management.call_command('cleanup_snippets')
|
||||
self.assertEqual(Snippet.objects.count(), 1)
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from django.views.defaults import (page_not_found as django_page_not_found,
|
|||
server_error as django_server_error)
|
||||
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.highlight import LEXER_WORDWRAP, LEXER_LIST
|
||||
from dpaste.highlight import LEXER_DEFAULT, LEXER_KEYS
|
||||
|
@ -285,6 +285,7 @@ FORMAT_MAPPING = {
|
|||
def snippet_api(request):
|
||||
content = request.POST.get('content', '').strip()
|
||||
lexer = request.REQUEST.get('lexer', LEXER_DEFAULT).strip()
|
||||
expires = request.REQUEST.get('expires', '').strip()
|
||||
format = request.REQUEST.get('format', 'default').strip()
|
||||
|
||||
if not content:
|
||||
|
@ -294,10 +295,21 @@ def snippet_api(request):
|
|||
return HttpResponseBadRequest('Invalid lexer given. Valid lexers are: %s' %
|
||||
', '.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(
|
||||
content=content,
|
||||
lexer=lexer,
|
||||
expires=datetime.datetime.now()+datetime.timedelta(seconds=60*60*24*30)
|
||||
expires=expires,
|
||||
expire_type=expire_type,
|
||||
)
|
||||
s.save()
|
||||
|
||||
|
|
Loading…
Reference in a new issue