diff --git a/paste/admin.py b/paste/admin.py index 8977c34..9d90129 100644 --- a/paste/admin.py +++ b/paste/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from .models import Paste + @admin.register(Paste) class PasteAdmin(admin.ModelAdmin): - list_display = ('paste_time', 'slug', 'title', 'viewcount') + list_display = ("paste_time", "slug", "title", "viewcount") diff --git a/paste/context_processors.py b/paste/context_processors.py index d97d1c0..5a72eff 100644 --- a/paste/context_processors.py +++ b/paste/context_processors.py @@ -3,8 +3,9 @@ from django.conf import settings def app_details(request): """Passes settings details to the templates.""" - return {'APP_NAME': settings.APP_NAME, - 'APP_VERSION': settings.APP_VERSION, - 'DISPLAY_NAME': settings.DISPLAY_NAME, - 'PASTE': settings.PASTE, + return { + "APP_NAME": settings.APP_NAME, + "APP_VERSION": settings.APP_VERSION, + "DISPLAY_NAME": settings.DISPLAY_NAME, + "PASTE": settings.PASTE, } diff --git a/paste/forms.py b/paste/forms.py index 8d66525..d198c42 100644 --- a/paste/forms.py +++ b/paste/forms.py @@ -6,23 +6,31 @@ from .models import Paste, Language, EXPIRE_CHOICES class PasteForm(ModelForm): """Paste model form.""" - content = CharField(max_length=settings.PASTE['max_characters'], - strip=False, - widget=forms.Textarea) + + content = CharField( + max_length=settings.PASTE["max_characters"], strip=False, widget=forms.Textarea + ) class Meta: model = Paste - fields = ['language', 'title', 'password', 'content', 'lifetime', - 'lifecount', 'private'] + fields = [ + "language", + "title", + "password", + "content", + "lifetime", + "lifecount", + "private", + ] def save(self, commit=True): """Overwrites save method.""" paste = super(PasteForm, self).save(commit=False) paste.compute_size() - if not self.cleaned_data['title']: - paste.title = 'no title' - if self.cleaned_data['password']: - paste.set_password(self.cleaned_data['password']) + if not self.cleaned_data["title"]: + paste.title = "no title" + if self.cleaned_data["password"]: + paste.set_password(self.cleaned_data["password"]) if commit: paste.save() return paste diff --git a/paste/middleware.py b/paste/middleware.py index a89d3cd..2440701 100644 --- a/paste/middleware.py +++ b/paste/middleware.py @@ -1,16 +1,18 @@ -from django.contrib.sessions.middleware import SessionMiddleware as DjangoSessionMiddleware +from django.contrib.sessions.middleware import ( + SessionMiddleware as DjangoSessionMiddleware, +) class SessionMiddleware(DjangoSessionMiddleware): def process_request(self, request): - if 'admin' in request.path: + if "admin" in request.path: session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME) request.session = self.SessionStore(session_key) else: request.session = {} def process_response(self, request, response): - if 'admin' in request.path: + if "admin" in request.path: return super().process_response(request, response) else: return response diff --git a/paste/migrations/0001_initial.py b/paste/migrations/0001_initial.py index 036cda9..c5b2533 100644 --- a/paste/migrations/0001_initial.py +++ b/paste/migrations/0001_initial.py @@ -10,37 +10,81 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Language', + name="Language", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=200, unique=True)), - ('slug', models.SlugField(max_length=200, unique=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=200, unique=True)), + ("slug", models.SlugField(max_length=200, unique=True)), ], ), migrations.CreateModel( - name='Paste', + name="Paste", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('slug', models.SlugField(editable=False, unique=True)), - ('title', models.CharField(blank=True, max_length=200)), - ('content', models.TextField(validators=[django.core.validators.MaxLengthValidator(100000)])), - ('size', models.IntegerField(default=0, editable=False)), - ('paste_time', models.DateTimeField(default=datetime.datetime.now, editable=False)), - ('paste_ip', models.GenericIPAddressField(editable=False)), - ('paste_agent', models.CharField(editable=False, max_length=200)), - ('lifetime', models.IntegerField(choices=[(0, 'Never expire'), (5, '5 minutes'), (30, '30 minutes'), (60, '1 hour'), (1440, '1 day'), (10080, '1 week')], default=0)), - ('lifecount', models.IntegerField(default=0)), - ('viewcount', models.IntegerField(default=0, editable=False)), - ('expired', models.BooleanField(default=False, editable=False)), - ('private', models.BooleanField(default=False)), - ('password', models.CharField(blank=True, max_length=128)), - ('salt', models.CharField(blank=True, max_length=36)), - ('language', models.ForeignKey(default=13, null=True, on_delete=django.db.models.deletion.SET_NULL, to='paste.Language')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("slug", models.SlugField(editable=False, unique=True)), + ("title", models.CharField(blank=True, max_length=200)), + ( + "content", + models.TextField( + validators=[django.core.validators.MaxLengthValidator(100000)] + ), + ), + ("size", models.IntegerField(default=0, editable=False)), + ( + "paste_time", + models.DateTimeField(default=datetime.datetime.now, editable=False), + ), + ("paste_ip", models.GenericIPAddressField(editable=False)), + ("paste_agent", models.CharField(editable=False, max_length=200)), + ( + "lifetime", + models.IntegerField( + choices=[ + (0, "Never expire"), + (5, "5 minutes"), + (30, "30 minutes"), + (60, "1 hour"), + (1440, "1 day"), + (10080, "1 week"), + ], + default=0, + ), + ), + ("lifecount", models.IntegerField(default=0)), + ("viewcount", models.IntegerField(default=0, editable=False)), + ("expired", models.BooleanField(default=False, editable=False)), + ("private", models.BooleanField(default=False)), + ("password", models.CharField(blank=True, max_length=128)), + ("salt", models.CharField(blank=True, max_length=36)), + ( + "language", + models.ForeignKey( + default=13, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="paste.Language", + ), + ), ], ), ] diff --git a/paste/migrations/0002_auto_20180513_1644.py b/paste/migrations/0002_auto_20180513_1644.py index 88fcc88..03abfdc 100644 --- a/paste/migrations/0002_auto_20180513_1644.py +++ b/paste/migrations/0002_auto_20180513_1644.py @@ -7,31 +7,40 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('paste', '0001_initial'), + ("paste", "0001_initial"), ] operations = [ - migrations.RemoveField( - model_name='paste', - name='paste_agent', - ), - migrations.RemoveField( - model_name='paste', - name='paste_ip', + migrations.RemoveField(model_name="paste", name="paste_agent",), + migrations.RemoveField(model_name="paste", name="paste_ip",), + migrations.AlterField( + model_name="paste", + name="language", + field=models.ForeignKey( + default=14, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="paste.Language", + ), ), migrations.AlterField( - model_name='paste', - name='language', - field=models.ForeignKey(default=14, null=True, on_delete=django.db.models.deletion.SET_NULL, to='paste.Language'), - ), - migrations.AlterField( - model_name='paste', - name='lifecount', + model_name="paste", + name="lifecount", field=models.IntegerField(blank=True, default=0), ), migrations.AlterField( - model_name='paste', - name='lifetime', - field=models.IntegerField(choices=[(0, 'Never expire'), (60, '1 hour'), (1440, '1 day'), (10080, '1 week'), (302400, '1 month'), (3679200, '1 year')], default=3679200), + model_name="paste", + name="lifetime", + field=models.IntegerField( + choices=[ + (0, "Never expire"), + (60, "1 hour"), + (1440, "1 day"), + (10080, "1 week"), + (302400, "1 month"), + (3679200, "1 year"), + ], + default=3679200, + ), ), ] diff --git a/paste/models.py b/paste/models.py index ead7fc7..4061a11 100644 --- a/paste/models.py +++ b/paste/models.py @@ -9,17 +9,18 @@ import uuid EXPIRE_CHOICES = ( - (0, _('Never expire')), - (60, _('1 hour')), - (60 * 24, _('1 day')), - (60 * 24 * 7, _('1 week')), - (60 * 24 * 7 * 30, _('1 month')), - (60 * 24 * 7 * 365, _('1 year')), + (0, _("Never expire")), + (60, _("1 hour")), + (60 * 24, _("1 day")), + (60 * 24 * 7, _("1 week")), + (60 * 24 * 7 * 30, _("1 month")), + (60 * 24 * 7 * 365, _("1 year")), ) class Language(models.Model): """Language object.""" + name = models.CharField(max_length=200, unique=True) slug = models.SlugField(max_length=200, unique=True) @@ -28,7 +29,8 @@ class Language(models.Model): language = cls.objects.filter(name__icontains=name).first() if not language: language = cls.objects.filter( - name__iexact=settings.PASTE['default_language']).first() + name__iexact=settings.PASTE["default_language"] + ).first() return language def __unicode__(self): @@ -41,15 +43,20 @@ class Language(models.Model): class Paste(models.Model): """Paste object.""" - language = models.ForeignKey(Language, default=14, on_delete=models.SET_NULL, null=True) + + language = models.ForeignKey( + Language, default=14, on_delete=models.SET_NULL, null=True + ) slug = models.SlugField(unique=True, editable=False) title = models.CharField(max_length=200, blank=True) - content = models.TextField(validators=[MaxLengthValidator( - settings.PASTE['max_characters'])]) + content = models.TextField( + validators=[MaxLengthValidator(settings.PASTE["max_characters"])] + ) size = models.IntegerField(default=0, editable=False) paste_time = models.DateTimeField(default=datetime.now, editable=False) - lifetime = models.IntegerField(default=settings.PASTE['default_lifetime'], - choices=EXPIRE_CHOICES) + lifetime = models.IntegerField( + default=settings.PASTE["default_lifetime"], choices=EXPIRE_CHOICES + ) lifecount = models.IntegerField(default=0, blank=True) viewcount = models.IntegerField(default=0, editable=False) expired = models.BooleanField(default=False, editable=False) @@ -109,7 +116,7 @@ class Paste(models.Model): """Return hashed string.""" if not self.salt: self.salt = str(uuid.uuid1()) - return hashlib.sha512((raw+self.salt).encode()).hexdigest() + return hashlib.sha512((raw + self.salt).encode()).hexdigest() def set_password(self, raw): """Define a hashed password.""" @@ -126,6 +133,5 @@ class Paste(models.Model): return self.slug def __str__(self): - excerpt = repr(self.content[:100]) + ( - '...' if len(self.content) > 100 else '') + excerpt = repr(self.content[:100]) + ("..." if len(self.content) > 100 else "") return "{} - {} - {}".format(self.slug, self.title, excerpt) diff --git a/paste/renderers.py b/paste/renderers.py index 7e434e7..49c852a 100644 --- a/paste/renderers.py +++ b/paste/renderers.py @@ -9,27 +9,27 @@ from paste.tools import cache_exists, cache_fetch, cache_store def render_pygments(request, paste, data): """Renders Pygments template.""" - key = paste.slug+'_pygments.cache' + key = paste.slug + "_pygments.cache" if cache_exists(key): highlighted_content = cache_fetch(key) else: lexer = get_lexer_by_name(paste.language.slug) - formatter = HtmlFormatter(style='emacs') + formatter = HtmlFormatter(style="emacs") highlighted_content = highlight(paste.content, lexer, formatter) cache_store(key, highlighted_content) - data['paste'] = paste - data['highlighted'] = highlighted_content - rendered = loader.render_to_string('paste/show-pygments.html', data, request) + data["paste"] = paste + data["highlighted"] = highlighted_content + rendered = loader.render_to_string("paste/show-pygments.html", data, request) return HttpResponse(rendered) def render_form(request, paste, data): """Renders Form template.""" - data['paste'] = paste - rendered = loader.render_to_string('paste/show-form.html', data, request) + data["paste"] = paste + rendered = loader.render_to_string("paste/show-form.html", data, request) return HttpResponse(rendered) def render_raw(request, paste, data): """Renders RAW content.""" - return HttpResponse(paste.content, content_type='text/plain') + return HttpResponse(paste.content, content_type="text/plain") diff --git a/paste/templatetags/filters.py b/paste/templatetags/filters.py index e6c3877..8407046 100644 --- a/paste/templatetags/filters.py +++ b/paste/templatetags/filters.py @@ -2,12 +2,14 @@ from django import template register = template.Library() -@register.filter(name='add_class') + +@register.filter(name="add_class") def add_class(value, arg): value.field.widget.attrs.update({"class": arg}) return value -@register.filter(name='placeholder') + +@register.filter(name="placeholder") def placeholder(value, arg): value.field.widget.attrs.update({"placeholder": arg}) return value diff --git a/paste/tools.py b/paste/tools.py index dc04d82..1575a90 100644 --- a/paste/tools.py +++ b/paste/tools.py @@ -8,7 +8,7 @@ from .models import Paste def random_id(model): """Returns a short uuid for the slug of the given model.""" - uuid = random.choice('0123456789') + shortuuid.uuid() + uuid = random.choice("0123456789") + shortuuid.uuid() for i in range(3, len(uuid)): potential_uuid = uuid[:i] if not model.objects.filter(slug=potential_uuid): @@ -28,11 +28,11 @@ def cache_exists(key): def cache_store(key, value): """Store cache value for key.""" - with open(cache_get_filepath(key), 'w') as cache_file: + with open(cache_get_filepath(key), "w") as cache_file: cache_file.write(value) def cache_fetch(key): """Fetch cache value for key.""" - with open(cache_get_filepath(key), 'r') as cache_file: + with open(cache_get_filepath(key), "r") as cache_file: return cache_file.read() diff --git a/paste/urls.py b/paste/urls.py index e20302a..6ff0106 100644 --- a/paste/urls.py +++ b/paste/urls.py @@ -5,9 +5,13 @@ from webtools import settings urlpatterns = [ - url(r'^$', views.index, name='index'), - url(r'^history$', views.history, name='history'), - url(r'^static/(?P.*)', serve, {'document_root': settings.STATIC_ROOT}), - url(r'^paste/(?P[a-zA-Z0-9]+)/(?P[a-z]+)?$', views.show, name='paste'), - url(r'^(?P[0-9][a-zA-Z0-9]+)$', views.show, name='short_paste'), + url(r"^$", views.index, name="index"), + url(r"^history$", views.history, name="history"), + url(r"^static/(?P.*)", serve, {"document_root": settings.STATIC_ROOT}), + url( + r"^paste/(?P[a-zA-Z0-9]+)/(?P[a-z]+)?$", + views.show, + name="paste", + ), + url(r"^(?P[0-9][a-zA-Z0-9]+)$", views.show, name="short_paste"), ] diff --git a/paste/views.py b/paste/views.py index f14d8a9..b215d8b 100644 --- a/paste/views.py +++ b/paste/views.py @@ -12,74 +12,86 @@ from webtools import settings @csrf_exempt def index(request): """Displays form.""" - data = {'menu': 'index', - 'max_characters': settings.PASTE['max_characters']} - if request.method == 'POST': + data = {"menu": "index", "max_characters": settings.PASTE["max_characters"]} + if request.method == "POST": paste = Paste(slug=random_id(Paste)) if request.FILES: for language_name, any_file in request.FILES.items(): break language = Language.by_name(language_name) - form = PasteForm({'language': language.id, - 'title': any_file.name, - 'private': settings.PASTE['private_by_default'], - 'lifetime': settings.PASTE['default_lifetime'], - 'content': any_file.read().decode() - }, instance=paste) + form = PasteForm( + { + "language": language.id, + "title": any_file.name, + "private": settings.PASTE["private_by_default"], + "lifetime": settings.PASTE["default_lifetime"], + "content": any_file.read().decode(), + }, + instance=paste, + ) else: form = PasteForm(request.POST, instance=paste) if not form.is_valid(): - data['form'] = form - return render(request, 'paste/index.html', data) - form.save() # Some logic added to overrided method, see forms.py + data["form"] = form + return render(request, "paste/index.html", data) + form.save() # Some logic added to overrided method, see forms.py location = request.build_absolute_uri( - reverse('short_paste', kwargs={'slug': paste.slug})) - return HttpResponseRedirect(location, content=location + "\n", - content_type='text/plain') - data['form'] = PasteForm(initial={ - 'private': settings.PASTE['private_by_default'], - 'lifetime': settings.PASTE['default_lifetime'], - 'language': Language.by_name(settings.PASTE['default_language']).id}) - data['absolute_index_url'] = request.build_absolute_uri(reverse('index')) - return render(request, 'paste/index.html', data) + reverse("short_paste", kwargs={"slug": paste.slug}) + ) + return HttpResponseRedirect( + location, content=location + "\n", content_type="text/plain" + ) + data["form"] = PasteForm( + initial={ + "private": settings.PASTE["private_by_default"], + "lifetime": settings.PASTE["default_lifetime"], + "language": Language.by_name(settings.PASTE["default_language"]).id, + } + ) + data["absolute_index_url"] = request.build_absolute_uri(reverse("index")) + return render(request, "paste/index.html", data) -def show(request, slug, renderer='pygments'): +def show(request, slug, renderer="pygments"): """Display paste.""" # Fetching object paste = get_object_or_404(Paste, slug=slug) - data = {'title': paste.title,'slug': slug} + data = {"title": paste.title, "slug": slug} # Handling expiration if paste.is_expired(): - return render(request, 'paste/expired.html') + return render(request, "paste/expired.html") # Handling passwords if paste.password: - if 'password' in request.POST: - password = request.POST['password'] - elif 'password' in request.COOKIES: - password = request.COOKIES['password'] + if "password" in request.POST: + password = request.POST["password"] + elif "password" in request.COOKIES: + password = request.COOKIES["password"] else: password = None if not paste.pwd_match(password): - return render(request, 'paste/locked.html', data) + return render(request, "paste/locked.html", data) # Before rendering actions paste.incr_viewcount() # Handling rendering modes - if not renderer or renderer not in settings.PASTE['enabled_renderers']: - renderer = settings.PASTE['default_renderer'] - data['current_renderer'] = renderer - data['renderers'] = settings.PASTE['enabled_renderers'] - render_method = getattr(renderers, 'render_%s' % renderer) + if not renderer or renderer not in settings.PASTE["enabled_renderers"]: + renderer = settings.PASTE["default_renderer"] + data["current_renderer"] = renderer + data["renderers"] = settings.PASTE["enabled_renderers"] + render_method = getattr(renderers, "render_%s" % renderer) response = render_method(request, paste, data) # Responding - if 'password' in request.POST: - response.set_cookie('password', request.POST['password']) + if "password" in request.POST: + response.set_cookie("password", request.POST["password"]) return response def history(request): """Display last 25 public non-expired pastes.""" - pastes = Paste.objects.filter(private=False, expired=False).order_by('-pk')[:25] - data = {'pastes': pastes, 'menu': 'history', 'default_renderer': settings.PASTE['default_renderer']} - return render(request, 'paste/history.html', data) + pastes = Paste.objects.filter(private=False, expired=False).order_by("-pk")[:25] + data = { + "pastes": pastes, + "menu": "history", + "default_renderer": settings.PASTE["default_renderer"], + } + return render(request, "paste/history.html", data)