Allowing to paste multiple files.
This commit is contained in:
parent
8eefddfda9
commit
3266bd553c
|
@ -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
|
|
@ -4,29 +4,105 @@
|
|||
{% load compress %}
|
||||
{% block content %}
|
||||
<h1>Paf'Py</h1>
|
||||
<p>Et Paf.</p>
|
||||
<p>Et PAF !</p>
|
||||
|
||||
<p>Ce « pastebin-like » s’utilise 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">"</span><span class="nv">$1</span><span class="s2">=@</span><span class="nv">$1</span><span class="s2">"</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">"Content-Type: text/plain"</span>
|
||||
<span class="k">else</span>
|
||||
curl https://p.afpy.org/ <span class="s2">"</span><span class="si">${</span><span class="p">@/*/-F&=@&</span><span class="si">}</span><span class="s2">"</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
|
||||
|
|
|
@ -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()
|
||||
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()
|
||||
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(
|
||||
paste.compute_size()
|
||||
paste.save()
|
||||
pastes.append(
|
||||
(
|
||||
request.build_absolute_uri(
|
||||
reverse("short_paste", kwargs={"slug": paste.slug})
|
||||
),
|
||||
paste.size,
|
||||
paste.filename,
|
||||
)
|
||||
return HttpResponseRedirect(
|
||||
location, content=location + "\n", content_type="text/plain"
|
||||
)
|
||||
|
||||
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")
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
Django
|
||||
Pygments
|
||||
shortuuid
|
||||
tabulate
|
||||
django_compressor
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue