Allowing to paste multiple files.

This commit is contained in:
Julien Palard 2023-04-24 17:39:50 +02:00
parent 8eefddfda9
commit 3266bd553c
Signed by: mdk
GPG Key ID: 0EFC1AC1006886F8
5 changed files with 136 additions and 64 deletions

View File

@ -1,29 +0,0 @@
from django import forms
from django.forms import CharField, ModelForm
from webtools import settings
from .models import Paste
class PasteForm(ModelForm):
"""Paste model form."""
content = CharField(
max_length=settings.PASTE["max_characters"], strip=False, widget=forms.Textarea
)
class Meta:
model = Paste
fields = [
"filename",
"content",
]
def save(self, commit=True):
"""Overwrites save method."""
paste = super(PasteForm, self).save(commit=False)
paste.compute_size()
if commit:
paste.save()
return paste

View File

@ -4,29 +4,105 @@
{% load compress %}
{% block content %}
<h1>Paf'Py</h1>
<p>Et Paf.</p>
<p>Et PAF !</p>
<p>Ce « pastebin-like » sutilise en ligne de commande, soit avec <tt>curl</tt> :</p>
<h2>Envoyer un ou plusieurs fichiers</h2>
<div class="highlight"><pre><span></span>curl {{ request.build_absolute_uri }} <span class="o">-F</span>hello.py<span class="o">=</span>@hello.py
En utilisant des requêtes <tt>multipart/form-data</tt> il est possible
d'envoyer un fichier :
<div class="highlight"><pre><span></span>curl {{ request.build_absolute_uri }} <span class="o">-F</span>manage.py<span class="o">=</span>@manage.py
| URL | size | filename |
|-------------------------|--------|-----------|
| https://p.afpy.org/g3LE | 251 | manage.py |
</pre></div>
<p>Mais le mieux est de l'utiliser avec une fonction <tt>bash</tt> :</p>
ou plusieurs fichiers en même temps :
<div class="highlight"><pre><span></span>pafpy<span class="o">()</span>
<div class="highlight"><pre><span></span>curl {{ request.build_absolute_uri }} <span class="o">-F</span>manage.py<span class="o">=</span>@manage.py <span class="o">-F</span>requirements.txt<span class="o">=</span>@requirements.txt
| URL | size | filename |
|-------------------------|--------|------------------|
| https://p.afpy.org/g3LE | 251 | manage.py |
| https://p.afpy.org/k4oT | 547 | requirements.txt |
</pre></div>
C'est l'extension dans le nom du fichier qui permet de choisir la
coloration syntaxique pour chaque fichier.
<h2>Envoyer dans le corps d'une requête</h2>
Il est possible de coller un unique fichier via le corps d'une requête <tt>POST</tt> :
<div class="highlight"><pre>
$ cal | curl -XPOST --data-binary @- {{ request.build_absolute_uri }}
| URL | size | filename |
|-------------------------|--------|----------|
| https://p.afpy.org/mo8X | 184 | request |
</pre></div>
Dans ce cas, il est possible de choisir la coloration syntaxique via l'entête <tt>Content-Type</tt> :
<div class="highlight"><pre>
$ cal | curl -XPOST -H <span class="s2">"Content-Type: text/plain"</span> --data-binary @- {{ request.build_absolute_uri }}
| URL | size | filename |
|-------------------------|--------|-------------|
| https://p.afpy.org/dNuo | 184 | request.txt |
</pre></div>
<h2>Fonction <tt>bash</tt></h2>
<p>Pour ceux qui ne souhaitent pas rédiger des requêtes <tt>curl</tt>
toute la journée, voici une petite fonction <tt>bash</tt> :</p>
<div class="highlight"><pre><span></span>paf<span class="o">()</span>
<span class="o">{</span>
curl {{request.build_absolute_uri}} -F<span class="s2">&quot;</span><span class="nv">$1</span><span class="s2">=@</span><span class="nv">$1</span><span class="s2">&quot;</span>
<span class="k">if</span> <span class="o">[[</span> <span class="nv">$#</span> <span class="o">==</span> <span class="m">0</span> <span class="o">]]</span>
<span class="k">then</span>
curl https://p.afpy.org/ --data-binary @- -H <span class="s2">&quot;Content-Type: text/plain&quot;</span>
<span class="k">else</span>
curl https://p.afpy.org/ <span class="s2">&quot;</span><span class="si">${</span><span class="p">@/*/-F&amp;=@&amp;</span><span class="si">}</span><span class="s2">&quot;</span>
<span class="k">fi</span>
<span class="o">}</span>
</pre></div>
Demo :
Cette fonction est capable d'envoyer un fichier :
<div class="highlight"><pre><span></span>$ pafpy manage.py
https://p.afpy.org/g3LE
<div class="highlight"><pre><span></span>$ paf manage.py
| URL | size | filename |
|-------------------------|--------|-----------|
| https://p.afpy.org/g3LE | 251 | manage.py |
</pre></div>
On peut le voir dans un navigateur (avec de la coloration syntaxique) :
<a href="https://p.afpy.org/g3LE">https://p.afpy.org/g3LE</a>), ou on peut le télécharger "brut" :
plusieurs fichiers :
<div class="highlight"><pre><span></span>$ paf *.py
| URL | size | filename |
|-------------------------|--------|-----------------------|
| https://p.afpy.org/bvRV | 188 | admin.py |
| https://p.afpy.org/5uei | 296 | context_processors.py |
| https://p.afpy.org/Xg5a | 1419 | models.py |
| https://p.afpy.org/GkGS | 309 | urls.py |
| https://p.afpy.org/LVXL | 2730 | views.py |
</pre></div>
et même de lire sur <tt>stdin</tt> :
<div class="highlight"><pre><span></span>$ cal | paf
| URL | size | filename |
|-------------------------|--------|-------------|
| https://p.afpy.org/dNuo | 184 | request.txt |
</pre></div>
<h2>Accès aux collages</h2>
Chaque collage peut-être consulté dans un navigateur (où il est présenté avec de la coloration syntaxique :
<a href="https://p.afpy.org/g3LE">https://p.afpy.org/g3LE</a>), ou
être consulté en ligne de commande (où il est délivré brut) :
<div class="highlight"><pre><span></span>$ curl https://p.afpy.org/g3LE
#!/usr/bin/env python

View File

@ -1,5 +1,5 @@
import json
from functools import lru_cache
from mimetypes import common_types, types_map
import pygments
from django.http import HttpResponse, HttpResponseRedirect
@ -8,38 +8,60 @@ from django.template import RequestContext, loader
from django.urls import reverse
from django.views.decorators.csrf import csrf_exempt
from pygments.formatters import HtmlFormatter
from pygments.lexers import get_lexer_by_name
from pygments.lexers import get_lexer_by_name, get_lexer_for_filename
from tabulate import tabulate
from paste.forms import PasteForm
from paste.models import Paste
from webtools import settings
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()}
HARDCODED_TYPES_INV = {"text/plain": ".txt"}
TYPES_INV = NON_STANDARD_TYPES_INV | STANDARD_TYPES_INV | HARDCODED_TYPES_INV
def get_files(request):
"""Get one or multiple files from either a multipart/form-data or
raw request body with a Content-Type header."""
if request.FILES:
return request.FILES
content_type = request.headers.get("content-type", "")
ext = TYPES_INV.get(content_type, "")
return {"request" + ext: request}
@csrf_exempt
def index(request):
"""Displays form."""
if request.method == "GET":
return render(request, "paste/index.html")
paste = Paste()
paste.choose_slug()
if not request.FILES:
return HttpResponse(json.dumps({"error": "Please provide a file."}, indent=4))
(filename, the_file), *_ = request.FILES.items()
form = PasteForm(
{
"filename": filename.split("/")[-1].split("\\")[-1],
"content": the_file.read().decode(),
},
instance=paste,
)
if not form.is_valid():
return HttpResponse(json.dumps(form.errors, indent=4))
form.save()
location = request.build_absolute_uri(
reverse("short_paste", kwargs={"slug": paste.slug})
)
return HttpResponseRedirect(
location, content=location + "\n", content_type="text/plain"
if request.headers.get("Expect") == "100-continue":
return HttpResponse("")
pastes = []
for filename, the_file in get_files(request).items():
paste = Paste(
filename=filename.split("/")[-1].split("\\")[-1],
content=the_file.read().decode("UTF-8"),
)
paste.choose_slug()
paste.compute_size()
paste.save()
pastes.append(
(
request.build_absolute_uri(
reverse("short_paste", kwargs={"slug": paste.slug})
),
paste.size,
paste.filename,
)
)
if not pastes:
return HttpResponse("error: Please provide a file.")
return HttpResponse(
tabulate(pastes, headers=("URL", "size", "filename"), tablefmt="github") + "\n",
content_type="text/plain",
)
@ -58,7 +80,7 @@ def show(request, slug):
@lru_cache(1024)
def pygmentize(filename, filecontents):
try:
lexer = get_lexer_by_name(filename.split(".")[-1])
lexer = get_lexer_for_filename(filename)
except pygments.util.ClassNotFound:
lexer = get_lexer_by_name(settings.PASTE["default_language"])
formatter = HtmlFormatter(style="emacs")

View File

@ -1,4 +1,5 @@
Django
Pygments
shortuuid
tabulate
django_compressor

View File

@ -24,3 +24,5 @@ shortuuid==1.0.11
# via -r requirements.in
sqlparse==0.4.4
# via django
tabulate==0.9.0
# via -r requirements.in