mirror of
https://github.com/DarrenOfficial/dpaste.git
synced 2024-11-15 16:12:51 +11:00
Support for onetime snippets.
This commit is contained in:
parent
8a55139ae0
commit
88be3a6b1f
9 changed files with 129 additions and 12 deletions
|
@ -28,7 +28,7 @@ class SnippetForm(forms.ModelForm):
|
|||
choices=LEXER_LIST,
|
||||
)
|
||||
|
||||
expire_options = forms.ChoiceField(
|
||||
expires = forms.ChoiceField(
|
||||
label=_(u'Expires'),
|
||||
choices=EXPIRE_CHOICES,
|
||||
initial=EXPIRE_DEFAULT,
|
||||
|
@ -74,10 +74,18 @@ class SnippetForm(forms.ModelForm):
|
|||
raise forms.ValidationError('This snippet was identified as Spam.')
|
||||
return self.cleaned_data
|
||||
|
||||
def clean_expire_options(self):
|
||||
expires = self.cleaned_data['expire_options']
|
||||
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):
|
||||
|
@ -89,7 +97,9 @@ class SnippetForm(forms.ModelForm):
|
|||
|
||||
# Add expire datestamp. None indicates 'keep forever', use the default
|
||||
# null state of the db column for that.
|
||||
expires = self.cleaned_data['expire_options']
|
||||
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))
|
||||
|
|
|
@ -14,12 +14,13 @@ class Command(LabelCommand):
|
|||
def handle(self, *args, **options):
|
||||
deleteable_snippets = Snippet.objects.filter(
|
||||
expires__isnull=False,
|
||||
expire_type__in=[Snippet.EXPIRE_TIME, Snippet.EXPIRE_ONETIME],
|
||||
expires__lte=datetime.datetime.now()
|
||||
)
|
||||
sys.stdout.write(u"%s snippets gets deleted:\n" % deleteable_snippets.count())
|
||||
for d in deleteable_snippets:
|
||||
sys.stdout.write(u"- %s (%s)\n" % (d.secret_id, d.expires))
|
||||
if options.get('dry_run'):
|
||||
sys.stdout.write(u'Dry run - Doing nothing! *crossingfingers*\n')
|
||||
sys.stdout.write(u'Dry run - Not actually deleting snippets!\n')
|
||||
else:
|
||||
deleteable_snippets.delete()
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import datetime
|
||||
from south.db import db
|
||||
from south.v2 import SchemaMigration
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Migration(SchemaMigration):
|
||||
|
||||
def forwards(self, orm):
|
||||
# Adding field 'Snippet.expire_type'
|
||||
db.add_column('dpaste_snippet', 'expire_type',
|
||||
self.gf('django.db.models.fields.PositiveSmallIntegerField')(default=1),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Snippet.view_count'
|
||||
db.add_column('dpaste_snippet', 'view_count',
|
||||
self.gf('django.db.models.fields.PositiveIntegerField')(default=0),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'Snippet.expire_type'
|
||||
db.delete_column('dpaste_snippet', 'expire_type')
|
||||
|
||||
# Deleting field 'Snippet.view_count'
|
||||
db.delete_column('dpaste_snippet', 'view_count')
|
||||
|
||||
|
||||
models = {
|
||||
u'dpaste.snippet': {
|
||||
'Meta': {'ordering': "('-published',)", 'object_name': 'Snippet'},
|
||||
'content': ('django.db.models.fields.TextField', [], {}),
|
||||
'expire_type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
|
||||
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
|
||||
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||
'lexer': ('django.db.models.fields.CharField', [], {'default': "'python'", 'max_length': '30'}),
|
||||
u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': u"orm['dpaste.Snippet']"}),
|
||||
'published': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
|
||||
u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||
'secret_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
|
||||
u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
|
||||
'view_count': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['dpaste']
|
|
@ -1,4 +1,3 @@
|
|||
from datetime import datetime
|
||||
from random import SystemRandom
|
||||
|
||||
from django.db import models
|
||||
|
@ -14,15 +13,29 @@ L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4)
|
|||
T = getattr(settings, 'DPASTE_SLUG_CHOICES',
|
||||
'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890')
|
||||
|
||||
ONETIME_LIMIT = getattr(settings, 'DPASTE_ONETIME_LIMIT', 2)
|
||||
|
||||
def generate_secret_id(length=L, alphabet=T):
|
||||
return ''.join([R.choice(alphabet) for i in range(length)])
|
||||
|
||||
class Snippet(models.Model):
|
||||
EXPIRE_TIME = 1
|
||||
EXPIRE_KEEP = 2
|
||||
EXPIRE_ONETIME = 3
|
||||
EXPIRE_CHOICES = (
|
||||
(EXPIRE_TIME, _(u'Expire by timestamp')),
|
||||
(EXPIRE_KEEP, _(u'Keep Forever')),
|
||||
(EXPIRE_ONETIME, _(u'One time snippet')),
|
||||
)
|
||||
|
||||
secret_id = models.CharField(_(u'Secret ID'), max_length=255, blank=True, null=True)
|
||||
content = models.TextField(_(u'Content'))
|
||||
lexer = models.CharField(_(u'Lexer'), max_length=30, default=LEXER_DEFAULT)
|
||||
published = models.DateTimeField(_(u'Published'), auto_now_add=True)
|
||||
expire_type = models.PositiveSmallIntegerField(_(u'Expire Type'),
|
||||
choices=EXPIRE_CHOICES, default=EXPIRE_CHOICES[0][0])
|
||||
expires = models.DateTimeField(_(u'Expires'), blank=True, null=True)
|
||||
view_count = models.PositiveIntegerField(_('View count'), default=0)
|
||||
parent = models.ForeignKey('self', null=True, blank=True, related_name='children')
|
||||
|
||||
class Meta:
|
||||
|
@ -32,6 +45,13 @@ class Snippet(models.Model):
|
|||
def get_linecount(self):
|
||||
return len(self.content.splitlines())
|
||||
|
||||
@property
|
||||
def remaining_views(self):
|
||||
if self.expire_type == self.EXPIRE_ONETIME:
|
||||
remaining = ONETIME_LIMIT - self.view_count
|
||||
return remaining > 0 and remaining or 0
|
||||
return None
|
||||
|
||||
@property
|
||||
def is_single(self):
|
||||
return self.is_root_node() and not self.get_children()
|
||||
|
|
|
@ -12,6 +12,13 @@ tt strong {
|
|||
outline: 3px solid #ffe699;
|
||||
}
|
||||
|
||||
.message {
|
||||
margin: 0;
|
||||
padding: 10px 30px;
|
||||
background-color: gold;
|
||||
text-shadow: 0 1px 0 #ffea90;
|
||||
}
|
||||
|
||||
/* Custom container */
|
||||
.container-fluid {
|
||||
margin: 0 auto;
|
||||
|
|
|
@ -51,6 +51,9 @@
|
|||
======================================================================= -->
|
||||
<div class="btn-group snippet-options">
|
||||
<button disabled class="btn">Expires in: {{ snippet.expires|timeuntil }}</button>
|
||||
<span class="btn disabled">
|
||||
{% blocktrans count counter=snippet.view_count %}{{ counter }} View{% plural %}{{ counter }} Views{% endblocktrans %}
|
||||
</span>
|
||||
{% if snippet.pk|in_list:request.session.snippet_list %}
|
||||
<a class="btn" href="{% url "snippet_delete" snippet.secret_id %}" onclick="return confirm('{% trans "Really delete this snippet?" %}');"><i class="icon-trash"></i> {% trans "Delete Now" %}</a>
|
||||
{% endif %}
|
||||
|
@ -62,6 +65,19 @@
|
|||
rel="nofollow" title="Create a secret Gist"><i class="icon-share"></i> {% trans "Gist" %}</a>
|
||||
</div>
|
||||
|
||||
{% if snippet.expire_type == 3 %}
|
||||
<p class="message">
|
||||
{% trans "This is a one-time snippet." %}
|
||||
{% if snippet.remaining_views > 1 %}
|
||||
{% trans "It will automatically get deleted after {{ remaining }} further views." %}
|
||||
{% elif snippet.remaining_views == 1 %}
|
||||
{% trans "It will automatically get deleted after the next view." %}
|
||||
{% else %}
|
||||
{% trans "It was automatically deleted and cannot be viewed again." %}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<!-- ======================================================================
|
||||
Snippet
|
||||
======================================================================= -->
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
<div class="form-options-expire">
|
||||
{{ snippet_form.expire_options.errors }}
|
||||
{{ snippet_form.expires.errors }}
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-trash" title="{% trans "Expire in" %}"></i></span>
|
||||
{{ snippet_form.expire_options }}
|
||||
{{ snippet_form.expires }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -23,7 +23,7 @@ class SnippetTestCase(TestCase):
|
|||
return {
|
||||
'content': u"Hello Wörld.\n\tGood Bye",
|
||||
'lexer': LEXER_DEFAULT,
|
||||
'expire_options': EXPIRE_DEFAULT,
|
||||
'expires': EXPIRE_DEFAULT,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ from django.views.defaults import (page_not_found as django_page_not_found,
|
|||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from dpaste.forms import SnippetForm
|
||||
from dpaste.models import Snippet
|
||||
from dpaste.models import Snippet, ONETIME_LIMIT
|
||||
from dpaste.highlight import LEXER_WORDWRAP, LEXER_LIST
|
||||
from dpaste.highlight import LEXER_DEFAULT, LEXER_KEYS
|
||||
|
||||
|
@ -58,6 +58,15 @@ def snippet_details(request, snippet_id, template_name='dpaste/snippet_details.h
|
|||
"""
|
||||
snippet = get_object_or_404(Snippet, secret_id=snippet_id)
|
||||
|
||||
# One time snippet get deleted if the view count matches our limit
|
||||
if snippet.view_count >= ONETIME_LIMIT:
|
||||
snippet.delete()
|
||||
raise Http404()
|
||||
|
||||
# Increase the view count of the snippet
|
||||
snippet.view_count += 1
|
||||
snippet.save()
|
||||
|
||||
tree = snippet.get_root()
|
||||
tree = tree.get_descendants(include_self=True)
|
||||
|
||||
|
@ -67,13 +76,18 @@ def snippet_details(request, snippet_id, template_name='dpaste/snippet_details.h
|
|||
}
|
||||
|
||||
if request.method == "POST":
|
||||
snippet_form = SnippetForm(data=request.POST, request=request, initial=new_snippet_initial)
|
||||
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)
|
||||
snippet_form = SnippetForm(
|
||||
initial=new_snippet_initial,
|
||||
request=request)
|
||||
|
||||
template_context = {
|
||||
'snippet_form': snippet_form,
|
||||
|
|
Loading…
Reference in a new issue