Refactored settings to use AppConfig

This commit is contained in:
Martin Mahner 2018-06-22 12:37:04 +02:00
parent 053808f2bf
commit 3bd3a83fdd
8 changed files with 267 additions and 158 deletions

View file

@ -1,6 +1,209 @@
from django.apps import AppConfig
from django.apps import AppConfig, apps
from django.utils.translation import ugettext_lazy as _
class dpasteAppConfig(AppConfig):
name = 'dpaste'
verbose_name = 'dpaste'
# Integer. Length of the random slug for each new snippet. In the rare
# case an existing slug is generated again, the length will increase by
# one more character.
SLUG_LENGTH = 4
# String. A string of characters which are used to create the random slug.
SLUG_CHOICES = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890'
# String. The lexer key that is pre-selected in the dropdown. Note that
# this is only used if the user has not saved a snippet before, otherwise
LEXER_DEFAULT = 'python'
# Integer. Maximum number of bytes per snippet.
MAX_CONTENT_LENGTH = 250 * 1024 * 1024
# A tuple of seconds and a descriptive string used in the lexer
# expiration dropdown. Example::
#
# from django.utils.translation import ugettext_lazy as _
# DPASTE_EXPIRE_CHOICES = (
# (3600, _('In one hour')),
# (3600 * 24 * 7, _('In one week')),
# (3600 * 24 * 30, _('In one month')),
# (3600 * 24 * 30 * 12 * 100, _('100 Years')),
# )
#
# **Infinite snippets** are supported. You can keep snippets forever when
# you set the choice key to ``never``. The management command will ignore
# these snippets::
#
# from django.utils.translation import ugettext_lazy as _
# DPASTE_EXPIRE_CHOICES = (
# (3600, _('In one hour')),
# ('never', _('Never')),
# )
EXPIRE_CHOICES = (
('onetime', _('One-Time snippet')),
(3600, _('In one hour')),
(3600 * 24 * 7, _('In one week')),
(3600 * 24 * 30, _('In one month')),
('never', _('Never')),
)
# Default value for ``EXPIRE_CHOICES``
EXPIRE_DEFAULT = 3600 * 24 * 7
# **One-Time snippets** are supported. One-Time snippets are automatically
# deleted once a defined view count has reached (Default: ``2``). To
# enable one-time snippets you have to add a choice ``onetime`` to the
# expire choices::
#
# from django.utils.translation import ugettext_lazy as _
# DPASTE_EXPIRE_CHOICES = (
# ('onetime', _('One-Time snippet')),
# (3600, _('In one hour')),
# (3600 * 24 * 7, _('In one week')),
# (3600 * 24 * 30, _('In one month')),
# )
#
# You can also set the maximum view count after what the snippet gets
# deleted. The default is ``2``. One view is from the author, one view
# is from another user.
ONETIME_LIMIT = 2
# Lexers which have wordwrap enabled by default
LEXER_WORDWRAP = ('rst',)
@property
def BASE_URL(self, request=None):
"""
String. The full qualified hostname and path to the dpaste instance.
This is used to generate a link in the API response. If the "Sites"
framework is installed, it uses the current Site domain. Otherwise
it falls back to 'https://dpaste.de'
"""
if apps.is_installed('django.contrib.sites'):
from django.contrib.sites.shortcuts import get_current_site
site = get_current_site(request)
if site:
return 'https://{0}'.format(site.domain)
return 'https://dpaste.de'
# Key names of the default text and code lexer.
PLAIN_TEXT_SYMBOL = '_text'
PLAIN_CODE_SYMBOL = '_code'
@property
def TEXT_FORMATTER(self):
"""
Choices list with all "Text" lexer. Prepend keys with an underscore
so they don't accidentally clash with a Pygments Lexer name.
Each list contains a lexer tuple of:
(Lexer key,
Lexer Display Name,
Lexer Highlight Class)
If the Highlight Class is not given, PygmentsHighlighter is used.
"""
from dpaste.highlight import (
PlainTextHighlighter,
MarkdownHighlighter,
RestructuredTextHighlighter
)
return [
(self.PLAIN_TEXT_SYMBOL, 'Plain Text', PlainTextHighlighter),
('_markdown', 'Markdown', MarkdownHighlighter),
('_rst', 'reStructuredText', RestructuredTextHighlighter),
]
@property
def CODE_FORMATTER(self):
"""
Choices list with all "Code" Lexer. Each list
contains a lexer tuple of:
(Lexer key,
Lexer Display Name,
Lexer Highlight Class)
If the Highlight Class is not given, PygmentsHighlighter is used.
"""
from dpaste.highlight import (
PlainCodeHighlighter,
SolidityHighlighter
)
return [
(self.PLAIN_CODE_SYMBOL, 'Plain Code', PlainCodeHighlighter),
('abap', 'ABAP'),
('apacheconf', 'ApacheConf'),
('applescript', 'AppleScript'),
('as', 'ActionScript'),
('bash', 'Bash'),
('bbcode', 'BBCode'),
('c', 'C'),
('cpp', 'C++'),
('clojure', 'Clojure'),
('cobol', 'COBOL'),
('css', 'CSS'),
('cuda', 'CUDA'),
('dart', 'Dart'),
('delphi', 'Delphi'),
('diff', 'Diff'),
('django', 'Django'),
('erlang', 'Erlang'),
('fortran', 'Fortran'),
('go', 'Go'),
('groovy', 'Groovy'),
('haml', 'Haml'),
('haskell', 'Haskell'),
('html', 'HTML'),
('http', 'HTTP'),
('ini', 'INI'),
('irc', 'IRC'),
('java', 'Java'),
('js', 'JavaScript'),
('json', 'JSON'),
('lua', 'Lua'),
('make', 'Makefile'),
('mako', 'Mako'),
('mason', 'Mason'),
('matlab', 'Matlab'),
('modula2', 'Modula'),
('monkey', 'Monkey'),
('mysql', 'MySQL'),
('numpy', 'NumPy'),
('objc', 'Obj-C'),
('ocaml', 'OCaml'),
('perl', 'Perl'),
('php', 'PHP'),
('postscript', 'PostScript'),
('powershell', 'PowerShell'),
('prolog', 'Prolog'),
('properties', 'Properties'),
('puppet', 'Puppet'),
('python', 'Python'), # Default lexer
('r', 'R'),
('rb', 'Ruby'),
('rst', 'reStructuredText'),
('rust', 'Rust'),
('sass', 'Sass'),
('scala', 'Scala'),
('scheme', 'Scheme'),
('scilab', 'Scilab'),
('scss', 'SCSS'),
('smalltalk', 'Smalltalk'),
('smarty', 'Smarty'),
('solidity', 'Solidity', SolidityHighlighter),
('sql', 'SQL'),
('tcl', 'Tcl'),
('tcsh', 'Tcsh'),
('tex', 'TeX'),
('vb.net', 'VB.net'),
('vim', 'VimL'),
('xml', 'XML'),
('xquery', 'XQuery'),
('xslt', 'XSLT'),
('yaml', 'YAML'),
]

View file

@ -1,22 +1,13 @@
import datetime
from django import forms
from django.conf import settings
from django.apps import apps
from django.utils.translation import ugettext_lazy as _
from .highlight import LEXER_DEFAULT, LEXER_KEYS, LEXER_CHOICES
from .highlight import LEXER_CHOICES, LEXER_DEFAULT, LEXER_KEYS
from .models import Snippet
EXPIRE_CHOICES = getattr(settings, 'DPASTE_EXPIRE_CHOICES', (
('onetime', _('One-Time snippet')),
(3600, _('In one hour')),
(3600 * 24 * 7, _('In one week')),
(3600 * 24 * 30, _('In one month')),
('never', _('Never')),
))
EXPIRE_DEFAULT = getattr(settings, 'DPASTE_EXPIRE_DEFAULT', 3600)
MAX_CONTENT_LENGTH = getattr(settings, 'DPASTE_MAX_CONTENT_LENGTH', 250*1024*1024)
config = apps.get_app_config('dpaste')
def get_expire_values(expires):
@ -28,7 +19,7 @@ def get_expire_values(expires):
expires = None
else:
expire_type = Snippet.EXPIRE_TIME
expires = expires and expires or EXPIRE_DEFAULT
expires = expires and expires or config.EXPIRE_DEFAULT
expires = datetime.datetime.now() + datetime.timedelta(seconds=int(expires))
return expires, expire_type
@ -37,7 +28,7 @@ class SnippetForm(forms.ModelForm):
content = forms.CharField(
label=_('Content'),
widget=forms.Textarea(attrs={'placeholder': _('Awesome code goes here...')}),
max_length=MAX_CONTENT_LENGTH,
max_length=config.MAX_CONTENT_LENGTH,
strip=False
)
@ -49,8 +40,8 @@ class SnippetForm(forms.ModelForm):
expires = forms.ChoiceField(
label=_('Expires'),
choices=EXPIRE_CHOICES,
initial=EXPIRE_DEFAULT
choices=config.EXPIRE_CHOICES,
initial=config.EXPIRE_DEFAULT
)
# Honeypot field

View file

@ -1,6 +1,6 @@
from logging import getLogger
from django.conf import settings
from django.apps import apps
from django.template.defaultfilters import escape, linebreaksbr
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
@ -12,7 +12,7 @@ from pygments.lexers.python import PythonLexer
from pygments.util import ClassNotFound
logger = getLogger(__file__)
config = apps.get_app_config('dpaste')
class NakedHtmlFormatter(HtmlFormatter):
"""Pygments HTML formatter with no further HTML tags."""
@ -37,7 +37,7 @@ class Highlighter(object):
@staticmethod
def get_lexer_display_name(lexer_name, fallback=_('(Deprecated Lexer)')):
for l in TEXT_FORMATTER + CODE_FORMATTER:
for l in config.TEXT_FORMATTER + config.CODE_FORMATTER:
if l[0] == lexer_name:
return l[1]
return fallback
@ -111,7 +111,7 @@ class PlainCodeHighlighter(Highlighter):
class PygmentsHighlighter(Highlighter):
"""
Highlight code string with Pygments. The lexer is automaticially
Highlight code string with Pygments. The lexer is automatically
determined by the lexer name.
"""
formatter = NakedHtmlFormatter()
@ -149,7 +149,7 @@ def get_highlighter_class(lexer_name):
If no suitable highlighter is found, return the generic
PlainCode Highlighter.
"""
for c in TEXT_FORMATTER + CODE_FORMATTER:
for c in config.TEXT_FORMATTER + config.CODE_FORMATTER:
if c[0] == lexer_name:
if len(c) == 3:
return c[2]
@ -161,113 +161,19 @@ def get_highlighter_class(lexer_name):
# Lexer List
# -----------------------------------------------------------------------------
# Lexer list. Each list contains a lexer tuple of:
#
# (Lexer key,
# Lexer Display Name,
# Lexer Highlight Class)
#
# If the Highlight Class is not given, PygmentsHighlighter is used.
# Default Highlight Types
PLAIN_TEXT = '_text' # lexer name whats rendered as text (paragraphs)
PLAIN_CODE = '_code' # lexer name of code with no hihglighting
TEXT_FORMATTER = [
(PLAIN_TEXT, 'Plain Text', PlainTextHighlighter),
('_markdown', 'Markdown', MarkdownHighlighter),
('_rst', 'reStructuredText', RestructuredTextHighlighter),
#('_textile', 'Textile', MarkdownHighlighter),
]
CODE_FORMATTER = [
(PLAIN_CODE, 'Plain Code', PlainCodeHighlighter),
('abap', 'ABAP'),
('apacheconf', 'ApacheConf'),
('applescript', 'AppleScript'),
('as', 'ActionScript'),
('bash', 'Bash'),
('bbcode', 'BBCode'),
('c', 'C'),
('cpp', 'C++'),
('clojure', 'Clojure'),
('cobol', 'COBOL'),
('css', 'CSS'),
('cuda', 'CUDA'),
('dart', 'Dart'),
('delphi', 'Delphi'),
('diff', 'Diff'),
('django', 'Django'),
('erlang', 'Erlang'),
('fortran', 'Fortran'),
('go', 'Go'),
('groovy', 'Groovy'),
('haml', 'Haml'),
('haskell', 'Haskell'),
('html', 'HTML'),
('http', 'HTTP'),
('ini', 'INI'),
('irc', 'IRC'),
('java', 'Java'),
('js', 'JavaScript'),
('json', 'JSON'),
('lua', 'Lua'),
('make', 'Makefile'),
('mako', 'Mako'),
('mason', 'Mason'),
('matlab', 'Matlab'),
('modula2', 'Modula'),
('monkey', 'Monkey'),
('mysql', 'MySQL'),
('numpy', 'NumPy'),
('objc', 'Obj-C'),
('ocaml', 'OCaml'),
('perl', 'Perl'),
('php', 'PHP'),
('postscript', 'PostScript'),
('powershell', 'PowerShell'),
('prolog', 'Prolog'),
('properties', 'Properties'),
('puppet', 'Puppet'),
('python', 'Python'),
('r', 'R'),
('rb', 'Ruby'),
('rst', 'reStructuredText'),
('rust', 'Rust'),
('sass', 'Sass'),
('scala', 'Scala'),
('scheme', 'Scheme'),
('scilab', 'Scilab'),
('scss', 'SCSS'),
('smalltalk', 'Smalltalk'),
('smarty', 'Smarty'),
('solidity', 'Solidity', SolidityHighlighter),
('sql', 'SQL'),
('tcl', 'Tcl'),
('tcsh', 'Tcsh'),
('tex', 'TeX'),
('vb.net', 'VB.net'),
('vim', 'VimL'),
('xml', 'XML'),
('xquery', 'XQuery'),
('xslt', 'XSLT'),
('yaml', 'YAML'),
]
# Generat a list of Form choices of all lexer.
# Generate a list of Form choices of all lexer.
LEXER_CHOICES = (
(_('Text'), [i[:2] for i in TEXT_FORMATTER]),
(_('Code'), [i[:2] for i in CODE_FORMATTER])
(_('Text'), [i[:2] for i in config.TEXT_FORMATTER]),
(_('Code'), [i[:2] for i in config.CODE_FORMATTER])
)
# List of all Lexer Keys
LEXER_KEYS = [i[0] for i in TEXT_FORMATTER] + [i[0] for i in CODE_FORMATTER]
LEXER_KEYS = [i[0] for i in config.TEXT_FORMATTER] + \
[i[0] for i in config.CODE_FORMATTER]
# The default lexer which we fallback in case of
# an error or if not supplied in an API call.
LEXER_DEFAULT = getattr(settings, 'DPASTE_LEXER_DEFAULT', 'python')
LEXER_DEFAULT = config.LEXER_DEFAULT
# Lexers which have wordwrap enabled by default
LEXER_WORDWRAP = getattr(settings, 'DPASTE_LEXER_WORDWRAP',
('rst',)
)
LEXER_WORDWRAP = config.LEXER_WORDWRAP

View file

@ -1,6 +1,7 @@
from logging import getLogger
from random import SystemRandom
from django.conf import settings
from django.apps import apps
from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
@ -8,15 +9,19 @@ from six import python_2_unicode_compatible
from dpaste import highlight
config = apps.get_app_config('dpaste')
logger = getLogger(__file__)
R = SystemRandom()
ONETIME_LIMIT = getattr(settings, 'DPASTE_ONETIME_LIMIT', 2)
def generate_secret_id(length=None, alphabet=None, tries=0):
length = length or getattr(settings, 'DPASTE_SLUG_LENGTH', 4)
alphabet = alphabet or getattr(settings, 'DPASTE_SLUG_CHOICES',
'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ1234567890')
secret_id = ''.join([R.choice(alphabet) for i in range(length)])
def generate_secret_id(length=None, tries=0):
if tries >= 10000:
m = 'Tried to often to generate a unique slug. Inceease the slug length.'
logger.critical(m)
raise Exception(m)
secret_id = ''.join([R.choice(config.SLUG_CHOICES)
for i in range(config.SLUG_LENGTH)])
# Check if this slug already exists, if not, return this new slug
try:
@ -84,5 +89,6 @@ class Snippet(models.Model):
@property
def remaining_views(self):
if self.expire_type == self.EXPIRE_ONETIME:
remaining = ONETIME_LIMIT - self.view_count
remaining = config.ONETIME_LIMIT - self.view_count
return remaining > 0 and remaining or 0

View file

@ -1,12 +1,14 @@
# -*- encoding: utf-8 -*-
from django.urls import reverse
from django.apps import apps
from django.test import TestCase
from django.test.client import Client
from django.urls import reverse
from ..highlight import PLAIN_CODE
from ..models import Snippet
config = apps.get_app_config('dpaste')
class SnippetAPITestCase(TestCase):
@ -224,7 +226,7 @@ class SnippetAPITestCase(TestCase):
})
self.assertEqual(response.status_code, 200)
self.assertEqual(Snippet.objects.count(), 1)
self.assertEqual(Snippet.objects.all()[0].lexer, PLAIN_CODE)
self.assertEqual(Snippet.objects.all()[0].lexer, config.PLAIN_CODE_SYMBOL)
def test_filename_and_lexer_given(self):
"""

View file

@ -4,8 +4,8 @@ from textwrap import dedent
from django.test import TestCase
from dpaste.highlight import PLAIN_CODE, PygmentsHighlighter, \
PlainCodeHighlighter, RestructuredTextHighlighter
from dpaste.highlight import PlainCodeHighlighter, PygmentsHighlighter, \
RestructuredTextHighlighter
class HighlightAPITestCase(TestCase):

View file

@ -2,16 +2,17 @@
from datetime import timedelta
from django.apps import apps
from django.core import management
from django.urls import reverse
from django.test import TestCase
from django.test.client import Client
from django.test.utils import override_settings
from django.urls import reverse
from ..forms import EXPIRE_DEFAULT
from ..highlight import LEXER_DEFAULT, PLAIN_CODE, PLAIN_TEXT, PygmentsHighlighter
from ..highlight import PygmentsHighlighter
from ..models import Snippet
config = apps.get_app_config('dpaste')
class SnippetTestCase(TestCase):
@ -22,8 +23,8 @@ class SnippetTestCase(TestCase):
def valid_form_data(self, **kwargs):
data = {
'content': u"Hello Wörld.\n\tGood Bye",
'lexer': LEXER_DEFAULT,
'expires': EXPIRE_DEFAULT,
'lexer': config.LEXER_DEFAULT,
'expires': config.EXPIRE_DEFAULT,
}
if kwargs:
data.update(kwargs)
@ -216,13 +217,15 @@ class SnippetTestCase(TestCase):
def test_xss_text_lexer(self):
# Simple 'text' lexer
data = self.valid_form_data(content=self.XSS_ORIGINAL, lexer=PLAIN_TEXT)
data = self.valid_form_data(content=self.XSS_ORIGINAL,
lexer=config.PLAIN_TEXT_SYMBOL)
response = self.client.post(self.new_url, data, follow=True)
self.assertContains(response, self.XSS_ESCAPED)
def test_xss_code_lexer(self):
# Simple 'code' lexer
data = self.valid_form_data(content=self.XSS_ORIGINAL, lexer=PLAIN_CODE)
data = self.valid_form_data(content=self.XSS_ORIGINAL,
lexer=config.PLAIN_CODE_SYMBOL)
response = self.client.post(self.new_url, data, follow=True)
self.assertContains(response, self.XSS_ESCAPED)
@ -313,7 +316,6 @@ class SnippetTestCase(TestCase):
PygmentsHighlighter().highlight('code', 'doesnotexist')
@override_settings(DPASTE_SLUG_LENGTH=1)
def test_random_slug_generation(self):
"""
Set the max length of a slug to 1, so we wont have more than 60

View file

@ -2,7 +2,7 @@ import datetime
import difflib
import json
from django.conf import settings
from django.apps import apps
from django.db.models import Count
from django.http import (Http404, HttpResponse, HttpResponseBadRequest,
HttpResponseRedirect)
@ -18,9 +18,11 @@ from pygments.lexers import get_lexer_for_filename
from pygments.util import ClassNotFound
from dpaste import highlight
from dpaste.forms import EXPIRE_CHOICES, SnippetForm, get_expire_values
from dpaste.forms import SnippetForm, get_expire_values
from dpaste.highlight import PygmentsHighlighter
from dpaste.models import ONETIME_LIMIT, Snippet
from dpaste.models import Snippet
config = apps.get_app_config('dpaste')
# -----------------------------------------------------------------------------
@ -78,7 +80,7 @@ class SnippetDetailView(SnippetView, DetailView):
# One-Time snippet get deleted if the view count matches our limit
if snippet.expire_type == Snippet.EXPIRE_ONETIME \
and snippet.view_count >= ONETIME_LIMIT:
and snippet.view_count >= config.ONETIME_LIMIT:
snippet.delete()
raise Http404()
@ -201,30 +203,27 @@ class AboutView(TemplateView):
def _format_default(s):
"""The default response is the snippet URL wrapped in quotes."""
return '"%s%s"' % (BASE_URL, s.get_absolute_url())
return '"%s%s"' % (config.BASE_URL, s.get_absolute_url())
def _format_url(s):
"""The `url` format returns the snippet URL, no quotes, but a linebreak after."""
return '%s%s\n' % (BASE_URL, s.get_absolute_url())
return '%s%s\n' % (config.BASE_URL, s.get_absolute_url())
def _format_json(s):
"""The `json` format export."""
return json.dumps({
'url': '%s%s' % (BASE_URL, s.get_absolute_url()),
'url': '%s%s' % (config.BASE_URL, s.get_absolute_url()),
'content': s.content,
'lexer': s.lexer,
})
BASE_URL = getattr(settings, 'DPASTE_BASE_URL', 'https://dpaste.de')
FORMAT_MAPPING = {
'default': _format_default,
'url': _format_url,
'json': _format_json,
}
class APIView(View):
"""
API View
@ -257,10 +256,10 @@ class APIView(View):
lexer_cls = get_lexer_for_filename(filename)
lexer = lexer_cls.aliases[0]
except (ClassNotFound, IndexError):
lexer = highlight.PLAIN_CODE
lexer = config.PLAIN_CODE_SYMBOL
if expires:
expire_options = [str(i) for i in dict(EXPIRE_CHOICES).keys()]
expire_options = [str(i) for i in dict(config.EXPIRE_CHOICES).keys()]
if not expires in expire_options:
return HttpResponseBadRequest('Invalid expire choice "{}" given. '
'Valid values are: {}'.format(expires, ', '.join(expire_options)))