Enable support for deleting entries through Micropub :D
This commit is contained in:
parent
427dcde672
commit
fa8419976d
4 changed files with 84 additions and 13 deletions
|
@ -146,6 +146,24 @@ class Entry(ModelMeta, TimeStampedModel):
|
|||
base = 'https://' + DjangoSite.objects.get_current().domain
|
||||
return urljoin(base, self.url)
|
||||
|
||||
@property
|
||||
def affected_urls(self):
|
||||
base = 'https://' + DjangoSite.objects.get_current().domain
|
||||
kind = kinds.from_id[self.kind]
|
||||
urls = {
|
||||
self.url,
|
||||
reverse('entries:index', kwargs={'kind': kind}),
|
||||
reverse('entries:atom_by_kind', kwargs={'kind': kind}),
|
||||
reverse('entries:rss_by_kind', kwargs={'kind': kind}),
|
||||
} | {cat.url for cat in self.cats.all()}
|
||||
if kind.on_home:
|
||||
urls |= {
|
||||
reverse('home:index'),
|
||||
reverse('entries:atom'),
|
||||
reverse('entries:rss')
|
||||
}
|
||||
return {urljoin(base, u) for u in urls}
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
kind = kinds.from_id[self.kind]
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import json
|
||||
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from lemonauth import tokens
|
||||
|
||||
from .create import create
|
||||
from .delete import delete
|
||||
from .query import query
|
||||
|
||||
actions = {
|
||||
'create': create,
|
||||
'delete': delete,
|
||||
}
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
@require_http_methods(['GET', 'HEAD', 'POST'])
|
||||
|
@ -14,7 +22,13 @@ def micropub(request):
|
|||
if hasattr(token, 'content'):
|
||||
return token
|
||||
request.token = token
|
||||
if request.method == 'POST':
|
||||
return create(request)
|
||||
if request.method in ('GET', 'HEAD'):
|
||||
return query(request)
|
||||
|
||||
action = request.POST.get('action', 'create')
|
||||
if request.content_type == 'application/json':
|
||||
request.json = json.load(request)
|
||||
action = request.json.get('action', 'create')
|
||||
if action in actions:
|
||||
return actions[action](request)
|
||||
return error.bad_req('unknown action: {}'.format(action))
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import json
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponse
|
||||
from urllib.parse import urljoin
|
||||
|
@ -30,7 +29,7 @@ def form_to_mf2(request):
|
|||
|
||||
def create(request):
|
||||
normalise = {
|
||||
'application/json': json.load,
|
||||
'application/json': lambda r: r.json,
|
||||
'application/x-www-form-urlencoded': form_to_mf2,
|
||||
}
|
||||
if 'create' not in request.token:
|
||||
|
@ -77,15 +76,7 @@ def create(request):
|
|||
base = utils.origin(request)
|
||||
perma = urljoin(base, entry.url)
|
||||
short = urljoin(base, entry.short_url)
|
||||
others = [urljoin(base, url) for url in (
|
||||
reverse('home:index'),
|
||||
reverse('entries:atom'),
|
||||
reverse('entries:rss'),
|
||||
reverse('entries:index', kwargs={'kind': kind}),
|
||||
reverse('entries:atom_by_kind', kwargs={'kind': kind}),
|
||||
reverse('entries:rss_by_kind', kwargs={'kind': kind}),
|
||||
)] + [urljoin(base, cat.url) for cat in cats]
|
||||
ping_hub.delay(perma, *others)
|
||||
ping_hub.delay(*entry.affected_urls)
|
||||
send_mentions.delay(perma)
|
||||
|
||||
res = HttpResponse(status=201)
|
||||
|
|
48
micropub/views/delete.py
Normal file
48
micropub/views/delete.py
Normal file
|
@ -0,0 +1,48 @@
|
|||
from django.http import HttpResponse
|
||||
from django.urls import resolve, Resolver404
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from entries.jobs import ping_hub
|
||||
from entries.models import Entry
|
||||
|
||||
from . import error
|
||||
|
||||
def delete(request):
|
||||
normalise = {
|
||||
'application/json': lambda r: r.json.get('url'),
|
||||
'application/x-www-form-urlencoded': lambda r: r.POST.get('url'),
|
||||
}
|
||||
if 'delete' not in request.token:
|
||||
return error.bad_scope('delete')
|
||||
if request.content_type not in normalise:
|
||||
return error.unsupported_type(request.content_type)
|
||||
url = normalise[request.content_type](request)
|
||||
if not url:
|
||||
return error.bad_req('url parameter required')
|
||||
|
||||
if '//' not in url:
|
||||
url = '//' + url
|
||||
url = urlparse(url, scheme='https')
|
||||
|
||||
if url.scheme not in ('http', 'https') or url.netloc != request.site.domain:
|
||||
return error.bad_req('url does not point to this site')
|
||||
try:
|
||||
match = resolve(url.path)
|
||||
except Resolver404:
|
||||
return error.bad_req('url does not point to a valid page on this site')
|
||||
|
||||
if match.view_name != 'entries:entry':
|
||||
return error.bad_req('url does not point to an entry on this site')
|
||||
|
||||
try:
|
||||
entry = Entry.objects.get(pk=match.kwargs['id'])
|
||||
except Entry.DoesNotExist:
|
||||
return error.bad_req('url does not point to an existing entry')
|
||||
|
||||
if entry.author != request.token.user:
|
||||
return error.forbid('entry belongs to another user')
|
||||
|
||||
urls = entry.affected_urls
|
||||
entry.delete()
|
||||
ping_hub.delay(urls)
|
||||
return HttpResponse(status=204)
|
Loading…
Reference in a new issue