Group pastes from a same upload with the same slug.

This commit is contained in:
Julien Palard 2023-04-25 10:49:36 +02:00
parent 14c6436b23
commit 1b1c3a1a47
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
9 changed files with 59 additions and 33 deletions

View File

@ -3,6 +3,7 @@ from datetime import datetime, timedelta
import shortuuid import shortuuid
from django.core.validators import MaxLengthValidator from django.core.validators import MaxLengthValidator
from django.db import models from django.db import models
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -25,15 +26,35 @@ class Paste(models.Model):
"""Computes size.""" """Computes size."""
self.size = len(self.content) self.size = len(self.content)
def get_absolute_url(self):
return reverse("short_paste", kwargs={"slug": self.slug})
def incr_viewcount(self): def incr_viewcount(self):
"""Increment view counter.""" """Increment view counter."""
self.viewcount = self.viewcount + 1 self.viewcount = self.viewcount + 1
self.save() self.save()
def __str__(self): def __str__(self):
excerpt = repr(self.content[:100]) + ("..." if len(self.content) > 100 else "") excerpt = repr(self.content.split("\n")[0][:100]) + (
"..." if len(self.content) > 100 else ""
)
return f"{self.slug} - {excerpt}" return f"{self.slug} - {excerpt}"
@classmethod
def choose_prefix(cls, filenames):
"""Find a prefix free for all the given filenames.
Such as <prefix>/filename is unused.
"""
while True:
uuid = shortuuid.uuid()
for i in range(4, len(uuid) + 1):
potential_uuid = uuid[:i]
for filename in filenames:
slug = f"{potential_uuid}/{filename}"
if not any(cls.objects.filter(slug=slug) for filename in filenames):
return potential_uuid
def choose_slug(self): def choose_slug(self):
while True: while True:
uuid = shortuuid.uuid() uuid = shortuuid.uuid()

View File

@ -1,11 +1,12 @@
{% load i18n %}{% load compress %}<!doctype html> {% load i18n compress static %}
<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{% compress css %} {% compress css %}
<link href="/static/css/font-awesome.min.css" rel="stylesheet"> <link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet">
<link href="/static/css/pygments-highlight.css" rel="stylesheet"> <link href="{% static 'css/pygments-highlight.css' %}" rel="stylesheet">
{% endcompress %} {% endcompress %}
{% block extrastyle %}{% endblock %} {% block extrastyle %}{% endblock %}
<title>{% block title %}{{ DISPLAY_NAME }}{% endblock %}</title> <title>{% block title %}{{ DISPLAY_NAME }}{% endblock %}</title>

View File

@ -1,6 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% block extrastyle %} {% block extrastyle %}
<link href="/static/css/github-markdown.css" rel="stylesheet"> <link href="{% static 'css/github-markdown.css' %}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block title %}{{ filename }}{% endblock %} {% block title %}{{ filename }}{% endblock %}
{% block content %} {% block content %}

View File

@ -1,6 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% block extrastyle %} {% block extrastyle %}
<link href="/static/css/pasteque.css" rel="stylesheet"> <link href="{% static 'css/pasteque.css' %}" rel="stylesheet">
{% endblock %} {% endblock %}
{% block title %}{{ filename }}{% endblock %} {% block title %}{{ filename }}{% endblock %}
{% block content %} {% block content %}

View File

@ -6,6 +6,6 @@ from webtools import settings
urlpatterns = [ urlpatterns = [
path("", views.index, name="index"), path("", views.index, name="index"),
path("static/<path>", serve, {"document_root": settings.STATIC_ROOT}), path("::/static/<path>", serve, {"document_root": settings.STATIC_ROOT}),
path("<slug>", views.show, name="short_paste"), path("<path:slug>", views.show, name="short_paste"),
] ]

View File

@ -10,7 +10,6 @@ from markdown.extensions.codehilite import CodeHiliteExtension
from pygments.formatters import HtmlFormatter from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name, get_lexer_for_filename from pygments.lexers import get_lexer_by_name, get_lexer_for_filename
ALLOWED_TAGS = [ ALLOWED_TAGS = [
# Bleach Defaults # Bleach Defaults
"a", "a",

View File

@ -1,10 +1,9 @@
from functools import lru_cache from functools import lru_cache
from mimetypes import common_types, types_map from mimetypes import common_types, types_map
from django.http import HttpResponse, HttpResponseRedirect from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.template import RequestContext, loader from django.template import RequestContext, loader
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from tabulate import tabulate from tabulate import tabulate
@ -14,7 +13,8 @@ from webtools import settings
NON_STANDARD_TYPES_INV = {value: key for key, value in common_types.items()} NON_STANDARD_TYPES_INV = {value: key for key, value in common_types.items()}
STANDARD_TYPES_INV = {value: key for key, value in types_map.items()} STANDARD_TYPES_INV = {value: key for key, value in types_map.items()}
TYPES_INV = NON_STANDARD_TYPES_INV | STANDARD_TYPES_INV HARDCODED_TYPES_INV = {"text/plain": ".txt"}
TYPES_INV = NON_STANDARD_TYPES_INV | STANDARD_TYPES_INV | HARDCODED_TYPES_INV
def get_files(request): def get_files(request):
@ -24,7 +24,9 @@ def get_files(request):
return request.FILES return request.FILES
content_type = request.headers.get("content-type", "") content_type = request.headers.get("content-type", "")
ext = TYPES_INV.get(content_type, "") ext = TYPES_INV.get(content_type, "")
return {"request" + ext: request} if ext:
return {"request" + ext: request}
return {"": request}
@csrf_exempt @csrf_exempt
@ -35,36 +37,37 @@ def index(request):
if request.headers.get("Expect") == "100-continue": if request.headers.get("Expect") == "100-continue":
return HttpResponse("") return HttpResponse("")
pastes = [] pastes = []
for filename, the_file in get_files(request).items(): files = get_files(request)
prefix = Paste.choose_prefix(list(files.keys()))
for filename, the_file in files.items():
filename = filename.replace("\\", "/")
paste = Paste( paste = Paste(
filename=filename.split("/")[-1].split("\\")[-1], slug=f"{prefix}/{filename}".rstrip("/"),
filename=filename.split("/")[-1],
content=the_file.read().decode("UTF-8"), content=the_file.read().decode("UTF-8"),
) )
paste.choose_slug()
paste.compute_size() paste.compute_size()
paste.save() paste.save()
pastes.append( pastes.append(paste)
(
request.build_absolute_uri( table = [
reverse("short_paste", kwargs={"slug": paste.slug}) (
), request.build_absolute_uri(paste.get_absolute_url()),
paste.size, paste.size,
paste.filename, paste.filename,
)
) )
for paste in pastes
if not pastes: ]
return HttpResponse("error: Please provide a file.")
return HttpResponse( return HttpResponse(
tabulate(pastes, headers=("URL", "size", "filename"), tablefmt="github") + "\n", tabulate(table, headers=("URL", "size", "filename"), tablefmt="github") + "\n",
content_type="text/plain", content_type="text/plain",
) )
def show(request, slug): def show(request, slug):
"""Display paste.""" """Display paste."""
if slug.startswith("::"):
raise Http404()
paste = get_object_or_404(Paste, slug=slug) paste = get_object_or_404(Paste, slug=slug)
paste.incr_viewcount() paste.incr_viewcount()

View File

@ -40,7 +40,7 @@ MEDIA_URL = ""
COMPRESS_ROOT = os.path.join(SITE_ROOT, "static") COMPRESS_ROOT = os.path.join(SITE_ROOT, "static")
MEDIA_ROOT = os.path.join(SITE_ROOT, "assets") MEDIA_ROOT = os.path.join(SITE_ROOT, "assets")
STATIC_ROOT = os.path.join(SITE_ROOT, "static") STATIC_ROOT = os.path.join(SITE_ROOT, "static")
STATIC_URL = "/static/" STATIC_URL = "/::/static/"
STATICFILES_FINDERS = ( STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder",

View File

@ -2,6 +2,6 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path("::/admin/", admin.site.urls),
path('', include('paste.urls')), path("", include("paste.urls")),
] ]