From 8d10c0bd1e60289ebcca2fb4e038a7b3c6c64ca2 Mon Sep 17 00:00:00 2001 From: Martin Mahner Date: Tue, 27 Mar 2018 18:42:54 +0200 Subject: [PATCH] POST based snippet delete --- client/scss/_globals.scss | 32 +++- client/scss/_mixins.scss | 14 ++ client/scss/components/_article.scss | 11 +- dpaste/templates/dpaste/details.html | 18 ++- dpaste/templates/dpaste/history.html | 20 ++- dpaste/templates/dpaste/includes/form.html | 2 +- dpaste/urls/dpaste.py | 12 +- dpaste/views.py | 180 +++++++++++---------- 8 files changed, 172 insertions(+), 117 deletions(-) diff --git a/client/scss/_globals.scss b/client/scss/_globals.scss index fb69123..79e1190 100644 --- a/client/scss/_globals.scss +++ b/client/scss/_globals.scss @@ -11,7 +11,6 @@ body[data-code-snippet] { background-color: $codeBgColor; } body[data-platform=win] .platform-mac { display: none; } body[data-platform=mac] .platform-win { display: none; } - .btn { padding: 6px 0; position: relative; @@ -45,3 +44,34 @@ body[data-platform=mac] .platform-win { display: none; } 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; + } +} diff --git a/client/scss/_mixins.scss b/client/scss/_mixins.scss index bfd64d7..7624601 100644 --- a/client/scss/_mixins.scss +++ b/client/scss/_mixins.scss @@ -5,3 +5,17 @@ border-right: 2px dotted $color; 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; + } +} diff --git a/client/scss/components/_article.scss b/client/scss/components/_article.scss index 4c8d688..da7615f 100644 --- a/client/scss/components/_article.scss +++ b/client/scss/components/_article.scss @@ -29,14 +29,5 @@ article { font-weight: $baseFontDemiBold; } - a:link, a:visited { - color: $linkColor; - text-decoration: underline; - text-decoration-color: lighten($linkColor, 30%); - } - - a:hover, a:active { - color: $hoverColor; - text-decoration: underline; - } + @include colored-links; } diff --git a/dpaste/templates/dpaste/details.html b/dpaste/templates/dpaste/details.html index 458b9b1..a7a8427 100644 --- a/dpaste/templates/dpaste/details.html +++ b/dpaste/templates/dpaste/details.html @@ -33,15 +33,8 @@
  • - {% trans "Delete Now" %} + {% trans "Delete Now" %}
  • - - {% if snippet.expire_type != 3 %}
  • {% trans "View Raw" %}
  • {% endif %} @@ -53,6 +46,15 @@ {% endif %} + +
    +
    + {% csrf_token %} + {% trans "Are you sure to delete this snippet?" %} + + {% trans "No, don't delete" %} +
    +
    {% endblock %} {% block page %} diff --git a/dpaste/templates/dpaste/history.html b/dpaste/templates/dpaste/history.html index 7c01ec2..b9cd9a8 100644 --- a/dpaste/templates/dpaste/history.html +++ b/dpaste/templates/dpaste/history.html @@ -11,18 +11,28 @@
  • {% trans "Snippet History" %}
  • {% if snippet_list %}
  • +
  • {% trans "Delete all Snippets" %}
  • - {% trans "Delete all Snippets" %} -
  • -
  • + {# Wordwrap is enabled by default for all lexer in the history #}
  • {% endif %} + +
    +
    + {% 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 %} + + {% trans "No, don't delete" %} +
    +
    {% endblock %} {% block page %} diff --git a/dpaste/templates/dpaste/includes/form.html b/dpaste/templates/dpaste/includes/form.html index d0aa4eb..fbb1bf5 100644 --- a/dpaste/templates/dpaste/includes/form.html +++ b/dpaste/templates/dpaste/includes/form.html @@ -24,7 +24,7 @@ {% trans "⌘+⏎" %} {% trans "Ctrl+⏎" %} - +

    diff --git a/dpaste/urls/dpaste.py b/dpaste/urls/dpaste.py index 8f6af38..38df9dc 100644 --- a/dpaste/urls/dpaste.py +++ b/dpaste/urls/dpaste.py @@ -8,14 +8,12 @@ from .. import views L = getattr(settings, 'DPASTE_SLUG_LENGTH', 4) urlpatterns = [ - url(r'^about/$', views.AboutView.as_view(), name='dpaste_about'), - 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'^delete/$', views.SnippetDeleteView.as_view(), name='snippet_delete'), - url(r'^(?P[a-zA-Z0-9]{%d,})/?$' % L, views.SnippetDetailView.as_view(), name='snippet_details'), - url(r'^(?P[a-zA-Z0-9]{%d,})/delete/$' % L, views.SnippetDeleteView.as_view(), name='snippet_delete'), - url(r'^(?P[a-zA-Z0-9]{%d,})/raw/?$' % L, views.SnippetRawView.as_view(), name='snippet_details_raw'), + url(r'^(?P[a-zA-Z0-9]{%d,})/?$' % L, + views.SnippetDetailView.as_view(), name='snippet_details'), + url(r'^(?P[a-zA-Z0-9]{%d,})/raw/?$' % L, + views.SnippetRawView.as_view(), name='snippet_details_raw'), ] diff --git a/dpaste/views.py b/dpaste/views.py index 3747423..e253647 100644 --- a/dpaste/views.py +++ b/dpaste/views.py @@ -66,6 +66,24 @@ class SnippetDetailView(SnippetView, DetailView): slug_url_kwarg = 'snippet_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): snippet = self.get_object() @@ -101,6 +119,7 @@ class SnippetDetailView(SnippetView, DetailView): }) return ctx + class SnippetRawView(SnippetDetailView): """ Display the raw content of a snippet @@ -113,22 +132,6 @@ class SnippetRawView(SnippetDetailView): 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): """ 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' - def get(self, request, *args, **kwargs): - snippet_id_list = request.session.get('snippet_list', []) - self.snippet_list = Snippet.objects.filter(pk__in=snippet_id_list) + def get_user_snippets(self): + snippet_id_list = self.request.session.get('snippet_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): ctx = super(SnippetHistory, self).get_context_data(**kwargs) ctx.update({ 'snippets_max': getattr(settings, 'DPASTE_MAX_SNIPPETS_PER_USER', 10), - 'snippet_list': self.snippet_list, + 'snippet_list': self.get_user_snippets(), }) return ctx -class SnippetDiffView(TemplateView): - """ - Display a diff between two given snippet secret ids. - """ - template_name = 'dpaste/includes/diff.html' - - def get(self, request, *args, **kwargs): - """ - Some validation around input files we will compare later. - """ - if request.GET.get('a') and request.GET.get('a').isdigit() \ - and request.GET.get('b') and request.GET.get('b').isdigit(): - try: - self.fileA = Snippet.objects.get(pk=int(request.GET.get('a'))) - self.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.') - - return super(SnippetDiffView, self).get(request, *args, **kwargs) - - def get_diff(self): - class DiffText(object): - pass - - diff = DiffText() - - if self.fileA.content != self.fileB.content: - d = difflib.unified_diff( - self.fileA.content.splitlines(), - self.fileB.content.splitlines(), - 'Original', - 'Current', - lineterm='' - ) - - diff.content = '\n'.join(d).strip() - diff.lexer = 'diff' - else: - diff.content = force_text(_(u'No changes were made between this two files.')) - diff.lexer = 'text' - - return diff - - def highlight_snippet(self, content): - h = highlight.pygmentize(content, 'diff') - h = h.replace(u'\t', '    ') - return h - - def get_context_data(self, **kwargs): - diff = self.get_diff() - highlighted = self.highlight_snippet(diff.content) - ctx = super(SnippetDiffView, self).get_context_data(**kwargs) - ctx.update({ - 'snippet': diff, - 'highlighted': highlighted.splitlines(), - 'fileA': self.fileA, - 'fileB': self.fileB, - }) - return ctx +# class SnippetDiffView(TemplateView): +# """ +# Display a diff between two given snippet secret ids. +# """ +# template_name = 'dpaste/includes/diff.html' +# +# def get(self, request, *args, **kwargs): +# """ +# Some validation around input files we will compare later. +# """ +# if request.GET.get('a') and request.GET.get('a').isdigit() \ +# and request.GET.get('b') and request.GET.get('b').isdigit(): +# try: +# self.fileA = Snippet.objects.get(pk=int(request.GET.get('a'))) +# self.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.') +# +# return super(SnippetDiffView, self).get(request, *args, **kwargs) +# +# def get_diff(self): +# class DiffText(object): +# pass +# +# diff = DiffText() +# +# if self.fileA.content != self.fileB.content: +# d = difflib.unified_diff( +# self.fileA.content.splitlines(), +# self.fileB.content.splitlines(), +# 'Original', +# 'Current', +# lineterm='' +# ) +# +# diff.content = '\n'.join(d).strip() +# diff.lexer = 'diff' +# else: +# diff.content = force_text(_(u'No changes were made between this two files.')) +# diff.lexer = 'text' +# +# return diff +# +# def highlight_snippet(self, content): +# h = highlight.pygmentize(content, 'diff') +# h = h.replace(u'\t', '    ') +# return h +# +# def get_context_data(self, **kwargs): +# diff = self.get_diff() +# highlighted = self.highlight_snippet(diff.content) +# ctx = super(SnippetDiffView, self).get_context_data(**kwargs) +# ctx.update({ +# 'snippet': diff, +# 'highlighted': highlighted.splitlines(), +# 'fileA': self.fileA, +# 'fileB': self.fileB, +# }) +# return ctx # -----------------------------------------------------------------------------