POST based snippet delete

This commit is contained in:
Martin Mahner 2018-03-27 18:42:54 +02:00
parent 38dd6f5c89
commit 8d10c0bd1e
8 changed files with 172 additions and 117 deletions

View file

@ -11,7 +11,6 @@ body[data-code-snippet] { background-color: $codeBgColor; }
body[data-platform=win] .platform-mac { display: none; } body[data-platform=win] .platform-mac { display: none; }
body[data-platform=mac] .platform-win { display: none; } body[data-platform=mac] .platform-win { display: none; }
.btn { .btn {
padding: 6px 0; padding: 6px 0;
position: relative; position: relative;
@ -45,3 +44,34 @@ body[data-platform=mac] .platform-win { display: none; }
top: 1px; top: 1px;
} }
} }
.confirm-modal {
@include colored-links;
background-color: $confirmBgColor;
color: $confirmTextColor;
// Hidden by default
overflow: hidden;
max-height: 0;
// Foldout animation
transition: max-height .15s ease-in;
form {
display: block;
padding: 20px $boxPadding;
}
.btn {
padding: 6px 15px;
margin: 0 10px;
}
.no {
font-size: 13px;
}
&:target {
max-height: 80px;
}
}

View file

@ -5,3 +5,17 @@
border-right: 2px dotted $color; border-right: 2px dotted $color;
margin: 0 ($margin+2px) 0 $margin; margin: 0 ($margin+2px) 0 $margin;
} }
@mixin colored-links() {
a:link, a:visited {
color: $linkColor;
text-decoration: underline;
text-decoration-color: lighten($linkColor, 30%);
}
a:hover, a:active {
color: $hoverColor;
text-decoration: underline;
}
}

View file

@ -29,14 +29,5 @@ article {
font-weight: $baseFontDemiBold; font-weight: $baseFontDemiBold;
} }
a:link, a:visited { @include colored-links;
color: $linkColor;
text-decoration: underline;
text-decoration-color: lighten($linkColor, 30%);
}
a:hover, a:active {
color: $hoverColor;
text-decoration: underline;
}
} }

View file

@ -33,15 +33,8 @@
</li> </li>
<li class="sep"></li> <li class="sep"></li>
<li> <li>
<a href="{% url "snippet_delete" snippet.secret_id %}" <a href="#delete">{% trans "Delete Now" %}</a>
onclick="return confirm('{% trans "Are you sure you want to delete this snippet?" %}');">{% trans "Delete Now" %}</a>
</li> </li>
<!--
{% if snippet.parent %}
<li><a href="#snippet-diff">{% trans "Compare with previous Snippet" %}</a></li>
{% endif %}
-->
{% if snippet.expire_type != 3 %} {% if snippet.expire_type != 3 %}
<li><a href="{% url "snippet_details_raw" snippet.secret_id %}">{% trans "View Raw" %}</a></li> <li><a href="{% url "snippet_details_raw" snippet.secret_id %}">{% trans "View Raw" %}</a></li>
{% endif %} {% endif %}
@ -53,6 +46,15 @@
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<div id="delete" class="confirm-modal">
<form method="POST" action="">
{% csrf_token %}
{% trans "Are you sure to delete this snippet?" %}
<button class="btn" name="delete" value="1" type="submit">{% trans "Yes, Delete" %}</button>
<a href="#" class="no">{% trans "No, don't delete" %}</a>
</form>
</div>
{% endblock %} {% endblock %}
{% block page %} {% block page %}

View file

@ -11,18 +11,28 @@
<li>{% trans "Snippet History" %}</li> <li>{% trans "Snippet History" %}</li>
{% if snippet_list %} {% if snippet_list %}
<li class="sep"></li> <li class="sep"></li>
<li><a href="#delete">{% trans "Delete all Snippets" %}</a></li>
<li> <li>
<a href="?delete-all"
onclick="return confirm('{% trans "Are you sure you want to delete all snippets?" %}');">{% trans "Delete all Snippets" %}</a>
</li>
<li>
<label for="wordwrap">
{# Wordwrap is enabled by default for all lexer in the history #} {# Wordwrap is enabled by default for all lexer in the history #}
<label for="wordwrap">
<input type="checkbox" id="wordwrap" checked> Wordwrap <input type="checkbox" id="wordwrap" checked> Wordwrap
</label> </label>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
<div id="delete" class="confirm-modal">
<form method="POST" action="">
{% csrf_token %}
{% blocktrans count snippet_list|length as count %}
Do you really want to delete the snippet below?
{% plural %}
Do you really want to delete the {{ count }} snippets below?
{% endblocktrans %}
<button class="btn" name="delete" value="1" type="submit">{% trans "Yes, Delete All" %}</button>
<a href="#" class="no">{% trans "No, don't delete" %}</a>
</form>
</div>
{% endblock %} {% endblock %}
{% block page %} {% block page %}

View file

@ -8,14 +8,12 @@ from .. import views
L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4) L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4)
urlpatterns = [ urlpatterns = [
url(r'^about/$', views.AboutView.as_view(), name='dpaste_about'),
url(r'^$', views.SnippetView.as_view(), name='snippet_new'), url(r'^$', views.SnippetView.as_view(), name='snippet_new'),
url(r'^diff/$', views.SnippetDiffView.as_view(), name='snippet_diff'), url(r'^about/$', views.AboutView.as_view(), name='dpaste_about'),
url(r'^history/$', views.SnippetHistory.as_view(), name='snippet_history'), 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,})/?$' % L,
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/delete/$' % L, views.SnippetDeleteView.as_view(), name='snippet_delete'), views.SnippetDetailView.as_view(), name='snippet_details'),
url(r'^(?P<snippet_id>[a-zA-Z0-9]{%d,})/raw/?$' % L, views.SnippetRawView.as_view(), 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

@ -66,6 +66,24 @@ class SnippetDetailView(SnippetView, DetailView):
slug_url_kwarg = 'snippet_id' slug_url_kwarg = 'snippet_id'
slug_field = 'secret_id' slug_field = 'secret_id'
def post(self, *args, **kwargs):
"""
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.
"""
if 'delete' in self.request.POST:
snippet = get_object_or_404(Snippet, secret_id=self.kwargs['snippet_id'])
snippet.delete()
# Append `#` so #delete goes away in Firefox
url = '{0}#'.format(reverse('snippet_new'))
return HttpResponseRedirect(url)
return super(SnippetDetailView, self).post(*args, **kwargs)
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
snippet = self.get_object() snippet = self.get_object()
@ -101,6 +119,7 @@ class SnippetDetailView(SnippetView, DetailView):
}) })
return ctx return ctx
class SnippetRawView(SnippetDetailView): class SnippetRawView(SnippetDetailView):
""" """
Display the raw content of a snippet Display the raw content of a snippet
@ -113,22 +132,6 @@ class SnippetRawView(SnippetDetailView):
return response return response
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.
"""
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'))
class SnippetHistory(TemplateView): class SnippetHistory(TemplateView):
""" """
Display the last `n` snippets created by this user (and saved in his Display the last `n` snippets created by this user (and saved in his
@ -136,85 +139,92 @@ class SnippetHistory(TemplateView):
""" """
template_name = 'dpaste/history.html' template_name = 'dpaste/history.html'
def get(self, request, *args, **kwargs): def get_user_snippets(self):
snippet_id_list = request.session.get('snippet_list', []) snippet_id_list = self.request.session.get('snippet_list', [])
self.snippet_list = Snippet.objects.filter(pk__in=snippet_id_list) return Snippet.objects.filter(pk__in=snippet_id_list)
def post(self, *args, **kwargs):
"""
Delete all user snippets at once.
"""
if 'delete' in self.request.POST:
self.get_user_snippets().delete()
# Append `#` so #delete goes away in Firefox
url = '{0}#'.format(reverse('snippet_history'))
return HttpResponseRedirect(url)
if 'delete-all' in request.GET:
self.snippet_list.delete()
return HttpResponseRedirect(reverse('snippet_history'))
return super(SnippetHistory, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
ctx = super(SnippetHistory, self).get_context_data(**kwargs) ctx = super(SnippetHistory, self).get_context_data(**kwargs)
ctx.update({ ctx.update({
'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10), 'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10),
'snippet_list': self.snippet_list, 'snippet_list': self.get_user_snippets(),
}) })
return ctx return ctx
class SnippetDiffView(TemplateView): # class SnippetDiffView(TemplateView):
""" # """
Display a diff between two given snippet secret ids. # Display a diff between two given snippet secret ids.
""" # """
template_name = 'dpaste/includes/diff.html' # template_name = 'dpaste/includes/diff.html'
#
def get(self, request, *args, **kwargs): # def get(self, request, *args, **kwargs):
""" # """
Some validation around input files we will compare later. # Some validation around input files we will compare later.
""" # """
if request.GET.get('a') and request.GET.get('a').isdigit() \ # if request.GET.get('a') and request.GET.get('a').isdigit() \
and request.GET.get('b') and request.GET.get('b').isdigit(): # and request.GET.get('b') and request.GET.get('b').isdigit():
try: # try:
self.fileA = Snippet.objects.get(pk=int(request.GET.get('a'))) # self.fileA = Snippet.objects.get(pk=int(request.GET.get('a')))
self.fileB = Snippet.objects.get(pk=int(request.GET.get('b'))) # self.fileB = Snippet.objects.get(pk=int(request.GET.get('b')))
except ObjectDoesNotExist: # except ObjectDoesNotExist:
return HttpResponseBadRequest(u'Selected file(s) does not exist.') # return HttpResponseBadRequest(u'Selected file(s) does not exist.')
else: # else:
return HttpResponseBadRequest(u'You must select two snippets.') # return HttpResponseBadRequest(u'You must select two snippets.')
#
return super(SnippetDiffView, self).get(request, *args, **kwargs) # return super(SnippetDiffView, self).get(request, *args, **kwargs)
#
def get_diff(self): # def get_diff(self):
class DiffText(object): # class DiffText(object):
pass # pass
#
diff = DiffText() # diff = DiffText()
#
if self.fileA.content != self.fileB.content: # if self.fileA.content != self.fileB.content:
d = difflib.unified_diff( # d = difflib.unified_diff(
self.fileA.content.splitlines(), # self.fileA.content.splitlines(),
self.fileB.content.splitlines(), # self.fileB.content.splitlines(),
'Original', # 'Original',
'Current', # 'Current',
lineterm='' # lineterm=''
) # )
#
diff.content = '\n'.join(d).strip() # diff.content = '\n'.join(d).strip()
diff.lexer = 'diff' # diff.lexer = 'diff'
else: # else:
diff.content = force_text(_(u'No changes were made between this two files.')) # diff.content = force_text(_(u'No changes were made between this two files.'))
diff.lexer = 'text' # diff.lexer = 'text'
#
return diff # return diff
#
def highlight_snippet(self, content): # def highlight_snippet(self, content):
h = highlight.pygmentize(content, 'diff') # h = highlight.pygmentize(content, 'diff')
h = h.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp;') # h = h.replace(u'\t', '&nbsp;&nbsp;&nbsp;&nbsp;')
return h # return h
#
def get_context_data(self, **kwargs): # def get_context_data(self, **kwargs):
diff = self.get_diff() # diff = self.get_diff()
highlighted = self.highlight_snippet(diff.content) # highlighted = self.highlight_snippet(diff.content)
ctx = super(SnippetDiffView, self).get_context_data(**kwargs) # ctx = super(SnippetDiffView, self).get_context_data(**kwargs)
ctx.update({ # ctx.update({
'snippet': diff, # 'snippet': diff,
'highlighted': highlighted.splitlines(), # 'highlighted': highlighted.splitlines(),
'fileA': self.fileA, # 'fileA': self.fileA,
'fileB': self.fileB, # 'fileB': self.fileB,
}) # })
return ctx # return ctx
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------