Branch: refs/heads/master Author: Enrico Tröger enrico.troeger@uvena.de Committer: Enrico Tröger enrico.troeger@uvena.de Date: Sun, 15 Jan 2023 11:44:27 UTC Commit: 144b4409781e1863f5d575b0d7421c0aac14deb0 https://github.com/geany/www.geany.org/commit/144b4409781e1863f5d575b0d7421c...
Log Message: ----------- Remove Pastebin form and related code
The Pastebin is spammed a lot and not of much use anyway. So remove it. Keep the API working for the GeniusPaste plugin.
Modified Paths: -------------- geany/settings.py geany/urls.py pastebin/api/create.py pastebin/forms.py pastebin/highlight.py pastebin/templates/pastebin/api.html pastebin/templates/pastebin/base.html pastebin/templates/pastebin/help.html pastebin/templates/pastebin/snippet_details.html pastebin/templates/pastebin/snippet_form.html pastebin/templates/pastebin/snippet_new.html pastebin/templates/pastebin/widgets/captcha_field.html pastebin/templates/pastebin/widgets/radio.html pastebin/templatetags/pastebin_tags.py pastebin/urls.py pastebin/views.py requirements.txt
Modified: geany/settings.py 8 lines changed, 0 insertions(+), 8 deletions(-) =================================================================== @@ -330,9 +330,7 @@
# 3rd party "compressor", - "captcha", # for pastebin "django_extensions", - "honeypot", # for pastebin "mezzanine_pagedown", "mezzanine_sync_pages.apps.MezzanineSyncPagesAppConfig", # "shortener", # disabled until it is fixed for Django 4.0 or we remove it completely @@ -434,12 +432,6 @@ 'compressor.filters.cssmin.CSSMinFilter'] COMPRESS_OFFLINE = True
-# django-honeypot -HONEYPOT_FIELD_NAME = 'website' -CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge' -CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',) -CAPTCHA_MATH_CHALLENGE_OPERATOR = '*' - NIGHTLYBUILDS_BASE_DIR = '/path/to/nightly/builds'
STATIC_DOCS_GITHUB_API_TOKEN = None
Modified: geany/urls.py 1 lines changed, 0 insertions(+), 1 deletions(-) =================================================================== @@ -61,7 +61,6 @@
# Pastebin path('p/', include('pastebin.urls')), - path('captcha/', include('captcha.urls')),
# URL Shortener # path('s/', include('urlshortener.urls')), # disabled until it is fixed for Django 4.0
Modified: pastebin/api/create.py 3 lines changed, 1 insertions(+), 2 deletions(-) =================================================================== @@ -102,7 +102,7 @@ def _validate_passed_fields(self): def _validate_against_snippet_form(self): self._preprocess_data()
- snippet_form = SnippetForm(request=self._request, data=self._data) + snippet_form = SnippetForm(data=self._data) # SnippetForm allows only a subset of lexer choices except the user enables all lexers # in her session but since we don't have a session here, we allow all unconditionally snippet_form.fields['lexer'].choices = LEXER_LIST_ALL @@ -115,7 +115,6 @@ def _validate_against_snippet_form(self):
# ---------------------------------------------------------------------- def _preprocess_data(self): - # compatibility with SnippetForm self._data['expire_options'] = self._data.get('expires', 3600) # try to map lexer from a Geany filetype name to a Pygments lexer name # fallback to 'text' as default, also use that default in case no lexer was given at all
Modified: pastebin/forms.py 60 lines changed, 7 insertions(+), 53 deletions(-) =================================================================== @@ -13,7 +13,6 @@
from datetime import timedelta
-from captcha.fields import CaptchaField, CaptchaTextInput from django import forms from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -36,15 +35,6 @@ EXPIRE_DEFAULT = 3600 * 24 * 30
-class ExpiryOptionsWidget(forms.RadioSelect): - template_name = 'pastebin/widgets/radio.html' - option_inherits_attrs = False - - -class CaptchaTextInputWidget(CaptchaTextInput): - template_name = 'pastebin/widgets/captcha_field.html' - - class SnippetForm(forms.ModelForm):
lexer = forms.ChoiceField( @@ -57,19 +47,15 @@ class SnippetForm(forms.ModelForm): choices=EXPIRE_CHOICES, initial=EXPIRE_DEFAULT, label=_('Expires'), - widget=ExpiryOptionsWidget, )
- captcha = CaptchaField(widget=CaptchaTextInputWidget(attrs={ - 'placeholder': 'Please solve the challenge'})) - - # ---------------------------------------------------------------------- - def __init__(self, request, *args, **kwargs): - forms.ModelForm.__init__(self, *args, **kwargs) - self.request = request - self.fields['captcha'].label = 'Verification' - # set author - self.fields['author'].initial = self.request.session.get('author', '') + class Meta: + model = Snippet + fields = ( + 'content', + 'title', + 'author', + 'lexer',)
# ---------------------------------------------------------------------- def _clean_field(self, field_name): @@ -92,35 +78,3 @@ def clean_content(self): # ---------------------------------------------------------------------- def clean_title(self): return self._clean_field('title') - - # ---------------------------------------------------------------------- - def save(self, *args, **kwargs): # pylint: disable=signature-differs - # Set parent snippet - parent = kwargs.pop('parent', None) - if parent: - self.instance.parent = parent - - # Add expire datestamp - self.instance.expires = timezone.now() + \ - timedelta(seconds=int(self.cleaned_data['expire_options'])) - - # Save snippet in the db - super().save(self, *args, **kwargs) - - # Add snippet to the user's session - if not self.request.session.get('snippet_list', False): - self.request.session['snippet_list'] = [] - self.request.session['snippet_list'].append(self.instance.pk) - - # Remember author - self.request.session['author'] = self.instance.author - - return self.request, self.instance - - class Meta: - model = Snippet - fields = ( - 'content', - 'title', - 'author', - 'lexer',)
Modified: pastebin/highlight.py 7 lines changed, 5 insertions(+), 2 deletions(-) =================================================================== @@ -61,8 +61,11 @@ def wrap(self, source):
# ---------------------------------------------------------------------- def _wrap_code(self, inner): - for code, text in inner: - yield code, text + yield from inner + + # ---------------------------------------------------------------------- + def _wrap_div(self, inner): + yield from inner
# ----------------------------------------------------------------------
Modified: pastebin/templates/pastebin/api.html 88 lines changed, 0 insertions(+), 88 deletions(-) =================================================================== @@ -1,88 +0,0 @@ -{% extends "pastebin/base.html" %} - -{% block meta_title %} - API Usage | {{ block.super }} -{% endblock %} - - -{% block main %} -<div class="well"> - -<h1>API</h1> - -<p>The following Python 3 code can be used as a simple command line client for this -Pastebin service:</p> - -<pre>#!/usr/bin/env python - -import urllib.request, urllib.parse, urllib.error -import urllib.request, urllib.error, urllib.parse -import os -import sys - -def paste_code(): - fields = [] - fields.append(('content', ''.join(sys.stdin.readlines()))) - fields.append(('author', os.getlogin())) - fields.append(('lexer', 'python')) - encoded_data = urllib.parse.urlencode(fields).encode('utf-8') - request = urllib.request.Request( - '{{ request.scheme }}://{{ request.get_host }}{% url 'snippet_api' %}', - encoded_data, - ) - response = urllib.request.urlopen(request) - response_content = response.read() - print(response_content.decode()) - -if __name__ == '__main__': - paste_code()</pre> - -<p>Save this script in <code>/usr/local/bin/gpaste</code> and <code>chmod +x /usr/local/bin/gpaste/</code>.</p> -<p>Usage: <code>cat foo.txt | gpaste</code></p> - -<h2>Supported fields</h2> -<p>The only mandatory field is <strong>content</strong> which should contain the text to be pasted.</p> -<p>Other accepted, optional fields are: -<table class="table table-striped table-bordered"> -<thead> - <tr> - <th>Field</th> - <th>Possible values</th> - <th>Description</th> - </tr> -</thead> -<tbody> - <tr> - <td class="nowrap">expires</td> - <td> - <ul> - <li>3600 (1 hour)</li> - <li>604800 (1 week)</li> - <li>2592000 (1 month)</li> - </ul> - </td> - <td>Time to live in seconds, defaults to 3600</td> - </tr> - <tr> - <td>title</td> - <td class="nowrap">Maximum 120 characters</td> - <td>The title of this paste</td> - </tr> - <tr> - <td>author</td> - <td class="nowrap">Maximum 30 characters</td> - <td>The author's name</td> - </tr> - <tr> - <td>lexer</td> - <td class="nowrap">python, text, php, perl, c, bash, ...</td> - <td>The lexer to be used, all lexers supported by Pygments are possible and - also most of Geany's filetype names (<code>geany --ft-names</code>) - </td> - </tr> -</tbody> -</table> -</p> - -</div> -{% endblock %}
Modified: pastebin/templates/pastebin/base.html 12 lines changed, 0 insertions(+), 12 deletions(-) =================================================================== @@ -8,9 +8,6 @@ {% endblock %}
{% block top_menu %} -{% url 'snippet_new' as url_snippet_new %} -{% url 'snippet_help' as url_help %} -{% url 'snippet_help_api' as url_api %} {% url 'snippet_list' as url_snippet_list %}
<div class="navbar navbar-inverse navbar-fixed-top"> @@ -27,15 +24,6 @@
<div class="navbar-collapse collapse" id="navbar"> <ul class="nav navbar-nav" id="navbar"> - <li class="{% if request.path == url_snippet_new %} active{% endif %}"> - <a href="{{ url_snippet_new }}">New snippet</a> - </li> - <li class="{% if request.path == url_help %} active{% endif %}"> - <a href="{{ url_help }}">Help</a> - </li> - <li class="{% if request.path == url_api %} active{% endif %}"> - <a href="{{ url_api }}">API</a> - </li> <li class="{% if request.path == url_snippet_list %} active{% endif %}"> <a href="{{ url_snippet_list }}">Recent snippets</a> </li>
Modified: pastebin/templates/pastebin/help.html 27 lines changed, 0 insertions(+), 27 deletions(-) =================================================================== @@ -1,27 +0,0 @@ -{% extends "pastebin/base.html" %} - -{% block meta_title %} - Help | {{ block.super }} -{% endblock %} - -{% block main %} -<div class="well"> -<h1>Frequently Asked Questions</h1> - -<h2>What is pastebin?</h2> -<p>This pastebin is here to help you collaborate on debugging code snippets. If you're not familiar with the idea, -most people use it like this:</p> -<ul> -<li><a href="{% url 'snippet_new' %}">submit</a> a code fragment to pastebin, -getting a url like {{ request.scheme }}://{{ request.get_host }}{% url 'snippet_details' 'abc1234' %}</li> -<li>paste the url into an IRC or IM conversation</li> -<li>someone responds by reading and perhaps submitting a modification of your code</li> -</ul> - -<h2>How can I delete a post?</h2> -<p>Your posted snippets are remembered in your session, you will be able to delete the snippet from the same browser you posted from - -simply view the post and click the "delete" link.</p> -<p>In other cases, contact us and we will delete it for you.</p> - -</div> -{% endblock %}
Modified: pastebin/templates/pastebin/snippet_details.html 15 lines changed, 5 insertions(+), 10 deletions(-) =================================================================== @@ -16,10 +16,6 @@ <h1> <div class="snippet-options"> <abbr title="Time to life">TTL:</abbr> {{ snippet.expires|timeuntil_or_forever }} — - {% if snippet.pk|in_list:request.session.snippet_list %} - <a onclick="return confirm('Really delete this snippet?')" href="{% url 'snippet_delete' snippet.secret_id %}">Delete</a> - — - {% endif %} {% if snippet.parent_id %} (Copy of <a href="{{ snippet.parent.get_absolute_url }}">snippet #{{ snippet.parent.id }}</a>) — @@ -31,14 +27,13 @@ <h1> <p>on <span class="date">{{ snippet.published|date:"Y/m/d G:i:s" }} (UTC)</span> by {% if snippet.author %}{{ snippet.author }}{% else %}Anonymous{% endif %} as {{ snippet.lexer|title }}</p> - <div class="tabbable"> - <ul class="nav nav-tabs"> - <li class="active"><a href="#tab1" data-toggle="tab">Markup</a></li> - <li><a href="#tab2" data-toggle="tab">Plain</a></li> + <ul class="nav nav-tabs" role="tablist"> + <li role="presentation" class="active"><a href="#markup" data-toggle="tab" aria-controls="markup" role="tab">Markup</a></li> + <li role="presentation"><a href="#plain" data-toggle="tab" aria-controls="plain" role="tab">Plain</a></li> </ul> <div class="tab-content"> - <div class="tab-pane active" id="tab1"> + <div class="tab-pane active" role="tabpanel" id="markup"> <div class="snippet"> <ol> {% for line in snippet|highlight %} @@ -49,7 +44,7 @@ <h1> </ol> </div> </div> - <div class="tab-pane" id="tab2"> + <div class="tab-pane" role="tabpanel" id="plain"> <div class="snippet-plain"> <textarea class="snippet-plain">{{ snippet.content }}</textarea> </div>
Modified: pastebin/templates/pastebin/snippet_form.html 41 lines changed, 0 insertions(+), 41 deletions(-) =================================================================== @@ -1,41 +0,0 @@ -{% load geany_tags honeypot i18n mezzanine_tags %} - -{% if snippet_form.non_field_errors or snippet_form.errors %} -<div class="form-errors"> - {% for error in snippet_form.non_field_errors %} - <div class="alert alert-danger non-field-error">{{ error }}</div> - {% empty %} - <div class="alert alert-danger field-error">{% trans "Please correct the errors below." %}</div> - {% endfor %} -</div> -{% endif %} - -<form method="post" action="{% url 'snippet_new' %}" class="form-horizontal new-snippet-form" role="form"> -{% csrf_token %} -{% render_honeypot_field %} -{% for field in snippet_form %} - <div> - <div class="form-group input_{{ field.id_for_label }} {{ field.field.type }} - {% if field.errors %} has-error{% endif %}"> - <label class="control-label col-sm-2" for="{{ field.auto_id }}">{{ field.label }}</label> - <div class="col-sm-10"> - {{ field|add_css:"form-control input-sm" }} - {% if field.errors %} - <p class="help-block"> - {% for e in field.errors %} - {% if not forloop.first %} / {% endif %}{{ e }} - {% endfor %} - </p> - {% endif %} - </div> - </div> - </div> -{% endfor %} -<div class="form-group"> - <div class="controls"> - <div class="col-sm-offset-2 col-sm-10"> - <button type="submit" class="btn btn-primary">{% trans "Paste it" %}</button> - </div> - </div> -</div> -</form>
Modified: pastebin/templates/pastebin/snippet_new.html 24 lines changed, 0 insertions(+), 24 deletions(-) =================================================================== @@ -1,24 +0,0 @@ -{% extends "pastebin/base.html" %} - -{% block extra_js %} -{{ block.super }} -<script> -$(function() {$('.mezzanine-form :input:visible:enabled:first').focus();}); -</script> -{% endblock %} - - -{% block meta_title %} - New snippet | {{ block.super }} -{% endblock %} - - -{% block main %} -<div class="well"> - <h1>Paste a new snippet</h1> - {% include "pastebin/snippet_form.html" %} - - {% include "pastebin/snippet_list_embedded.html" %} - -</div> -{% endblock %}
Modified: pastebin/templates/pastebin/widgets/captcha_field.html 6 lines changed, 0 insertions(+), 6 deletions(-) =================================================================== @@ -1,6 +0,0 @@ -{% spaceless %} -<div class="radio-inline new-snippet-form-no-padding-left""><img src="{{ image }}" alt="captcha" class="captcha" /></div> -<div class="radio-inline new-snippet-form-no-padding-left"> -{% include "django/forms/widgets/multiwidget.html" %} -</div> -{% endspaceless %}
Modified: pastebin/templates/pastebin/widgets/radio.html 5 lines changed, 0 insertions(+), 5 deletions(-) =================================================================== @@ -1,5 +0,0 @@ -{% with id=widget.attrs.id %}<div{% if id %} id="{{ id }}"{% endif %}{% if widget.attrs.class %} {{ widget.attrs.class }}"{% endif %}>{% for group, options, index in widget.optgroups %}{% if group %} - <div><label>{{ group }}</label>{% endif %}{% for option in options %}<div class="radio-inline"> - {% include option.template_name with widget=option %}</div>{% endfor %}{% if group %} - </div>{% endif %}{% endfor %} -</div>{% endwith %}
Modified: pastebin/templatetags/pastebin_tags.py 6 lines changed, 0 insertions(+), 6 deletions(-) =================================================================== @@ -25,12 +25,6 @@ register = Library()
-# ---------------------------------------------------------------------- -@register.filter -def in_list(value, arg): - return value in arg - - # ---------------------------------------------------------------------- @register.filter def timeuntil_or_forever(snippet_expire):
Modified: pastebin/urls.py 17 lines changed, 1 insertions(+), 16 deletions(-) =================================================================== @@ -18,35 +18,20 @@ from pastebin.views import ( LatestSnippetsView, SnippetAPIView, - SnippetDeleteView, SnippetDetailRawView, SnippetDetailView, - SnippetNewView, )
urlpatterns = ( # pylint: disable=invalid-name - path( - 'help/', - TemplateView.as_view(template_name='pastebin/help.html'), - name='snippet_help'), - path( - 'help/api/', - TemplateView.as_view(template_name='pastebin/api.html'), - name='snippet_help_api'), - path('api/', never_cache(SnippetAPIView.as_view()), name='snippet_api'),
- path('', never_cache(SnippetNewView.as_view()), name='snippet_new'), + path('', never_cache(LatestSnippetsView.as_view()), name='snippet_list'), path('latest/', never_cache(LatestSnippetsView.as_view()), name='snippet_list'), re_path( r'^(?P<snippet_id>[a-zA-Z0-9]+)/$', SnippetDetailView.as_view(), name='snippet_details'), - re_path( - r'^(?P<snippet_id>[a-zA-Z0-9]+)/delete/$', - SnippetDeleteView.as_view(), - name='snippet_delete'), re_path( r'^(?P<snippet_id>[a-zA-Z0-9]+)/raw/$', SnippetDetailRawView.as_view(),
Modified: pastebin/views.py 75 lines changed, 7 insertions(+), 68 deletions(-) =================================================================== @@ -15,29 +15,28 @@ from django.contrib.sites.models import Site from django.core.cache import cache from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseRedirect -from django.shortcuts import get_object_or_404, render +from django.http import HttpResponse, HttpResponseBadRequest +from django.shortcuts import render from django.template.response import TemplateResponse -from django.urls import reverse +from django.utils import timezone from django.utils.decorators import method_decorator from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.generic.base import TemplateView, View -from honeypot.decorators import check_honeypot
from geany.decorators import CACHE_TIMEOUT_24HOURS from pastebin.api.create import CreateSnippetApiController, SnippetValidationError -from pastebin.forms import SnippetForm from pastebin.models import CACHE_KEY_SNIPPET_LIST_FULL, CACHE_KEY_SNIPPET_LIST_NO_CONTENT, Snippet
# ---------------------------------------------------------------------- def _get_snippet_list(no_content=False): + base_queryset = Snippet.objects.filter(published__lte=timezone.now()) if no_content: - queryset = Snippet.objects.defer('content', 'content_highlighted') + queryset = base_queryset.defer('content', 'content_highlighted') cache_key = CACHE_KEY_SNIPPET_LIST_NO_CONTENT else: - queryset = Snippet.objects.all() + queryset = base_queryset.all() cache_key = CACHE_KEY_SNIPPET_LIST_FULL
# snippet list in cache? @@ -55,35 +54,6 @@ def _get_snippet_list(no_content=False): return snippet_list
-class SnippetNewView(View): - template_name = 'pastebin/snippet_new.html' - - # ---------------------------------------------------------------------- - @method_decorator(check_honeypot) - def dispatch(self, request, *args, **kwargs): - return super().dispatch(request, *args, **kwargs) - - # ---------------------------------------------------------------------- - def get(self, request): - snippet_form = SnippetForm(request=request) - return self._render_response(request, snippet_form) - - # ---------------------------------------------------------------------- - def _render_response(self, request, snippet_form): - snippet_list = _get_snippet_list(no_content=True) - template_context = dict(snippet_form=snippet_form, snippet_list=snippet_list) - return render(request, self.template_name, template_context) - - # ---------------------------------------------------------------------- - def post(self, request): - snippet_form = SnippetForm(data=request.POST, request=request) - if snippet_form.is_valid(): - request, new_snippet = snippet_form.save() - return HttpResponseRedirect(new_snippet.get_absolute_url()) - - return self._render_response(request, snippet_form) - - class SnippetNotFoundError(Exception): pass
@@ -92,7 +62,6 @@ class SnippetDetailView(View): template_name = 'pastebin/snippet_details.html'
# ---------------------------------------------------------------------- - @method_decorator(check_honeypot) def dispatch(self, request, *args, **kwargs): return super().dispatch(request, *args, **kwargs)
@@ -106,13 +75,9 @@ def get(self, request, snippet_id): context = dict(message=exc) return TemplateResponse(request, 'errors/404.html', context=context, status=404)
- new_snippet_initial = dict(content=snippet.content, lexer=snippet.lexer) - snippet_form = SnippetForm(initial=new_snippet_initial, request=request) - snippet_list_ = _get_snippet_list(no_content=True) template_context = { 'snippet_list': snippet_list_, - 'snippet_form': snippet_form, 'snippet': snippet, 'lines': range(snippet.get_linecount()), } @@ -122,7 +87,7 @@ def get(self, request, snippet_id): # ---------------------------------------------------------------------- def _fetch_snippet(self, snippet_id): try: - snippet = Snippet.objects.get(secret_id=snippet_id) + snippet = Snippet.objects.get(secret_id=snippet_id, published__lte=timezone.now()) except MultipleObjectsReturned as exc: raise SnippetNotFoundError( _('Multiple snippets exist for this slug. This should never happen.') @@ -146,32 +111,6 @@ def get(self, request, snippet_id): return response
-class SnippetDeleteView(View): - - # ---------------------------------------------------------------------- - def get(self, request, snippet_id): - snippet = get_object_or_404(Snippet, secret_id=snippet_id) - try: - snippet_list_ = request.session['snippet_list'] - except KeyError: - # 403 response with custom message - return TemplateResponse( - request, - 'errors/403.html', - context=dict(message=_('You have no recent snippet list, cookie error?')), - status=403) - if snippet.pk not in snippet_list_: - # 403 response with custom message - return TemplateResponse( - request, - 'errors/403.html', - context=dict(message=_('That is not your snippet!')), - status=403) - - snippet.delete() - return HttpResponseRedirect(reverse('snippet_new')) - - class LatestSnippetsView(TemplateView): template_name = 'pastebin/snippet_list.html'
Modified: requirements.txt 2 lines changed, 0 insertions(+), 2 deletions(-) =================================================================== @@ -4,10 +4,8 @@ mysqlclient django-clearcache django-compressor django-extensions -django-honeypot django-link-shortener django-log-request-id -django-simple-captcha mezzanine-pagedown mezzanine-sync-pages packaging
-------------- This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).