diff --git a/lemoncurry/settings/base.py b/lemoncurry/settings/base.py
index 2deb829..52eea8f 100644
--- a/lemoncurry/settings/base.py
+++ b/lemoncurry/settings/base.py
@@ -85,6 +85,7 @@ INSTALLED_APPS = [
'entries',
'home',
'lemonauth',
+ 'micropub',
'users',
'wellknowns',
]
diff --git a/lemoncurry/templates/lemoncurry/layout.html b/lemoncurry/templates/lemoncurry/layout.html
index 014c4dc..ee50daa 100644
--- a/lemoncurry/templates/lemoncurry/layout.html
+++ b/lemoncurry/templates/lemoncurry/layout.html
@@ -13,6 +13,7 @@
+
diff --git a/lemoncurry/urls.py b/lemoncurry/urls.py
index 27ccc89..6e3e5a7 100644
--- a/lemoncurry/urls.py
+++ b/lemoncurry/urls.py
@@ -39,6 +39,7 @@ urlpatterns = [
url('^.well-known/', include('wellknowns.urls')),
url('^admin/', otp_admin_site.urls),
url('^auth/', include('lemonauth.urls')),
+ url('^micropub', include('micropub.urls')),
url('^s/', include('shorturls.urls')),
url(r'^sitemap\.xml$', sitemap.index, maps, name='sitemap'),
diff --git a/micropub/__init__.py b/micropub/__init__.py
new file mode 100644
index 0000000..520e21e
--- /dev/null
+++ b/micropub/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'micropub.apps.MicropubConfig'
diff --git a/micropub/apps.py b/micropub/apps.py
new file mode 100644
index 0000000..d5259ee
--- /dev/null
+++ b/micropub/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class MicropubConfig(AppConfig):
+ name = 'micropub'
diff --git a/micropub/urls.py b/micropub/urls.py
new file mode 100644
index 0000000..b6c48bb
--- /dev/null
+++ b/micropub/urls.py
@@ -0,0 +1,7 @@
+from django.conf.urls import url
+from . import views
+
+app_name = 'micropub'
+urlpatterns = (
+ url('^$', views.micropub, name='micropub'),
+)
diff --git a/micropub/views.py b/micropub/views.py
new file mode 100644
index 0000000..35a726e
--- /dev/null
+++ b/micropub/views.py
@@ -0,0 +1,52 @@
+from django.contrib.auth import get_user_model
+from django.http import HttpResponse
+from django_push.publisher import ping_hub
+from django.urls import reverse
+from django.views.decorators.csrf import csrf_exempt
+from django.views.decorators.http import require_POST
+from urllib.parse import urljoin
+
+from entries.models import Entry
+from entries.kinds import Article, Note
+from lemoncurry import utils
+from lemonauth import tokens
+
+
+@csrf_exempt
+@require_POST
+def micropub(request):
+ auth = request.META.get('HTTP_AUTHORIZATION', '').split(' ')
+ if auth[0] != 'Bearer':
+ return utils.bad_req('only Bearer auth supported')
+ try:
+ token = tokens.decode(auth[1])
+ except Exception:
+ return utils.forbid('invalid token')
+ user = get_user_model().objects.get(pk=token['uid'])
+
+ post = request.POST
+ if post.get('h') != 'entry':
+ return utils.bad_req('only h=entry supported')
+ entry = Entry(author=user, kind=Note.id)
+ if 'name' in post:
+ entry.name = post['name']
+ entry.kind = Article.id
+ if 'content' in post:
+ entry.content = post['content']
+
+ entry.save()
+
+ base = utils.origin(request)
+ perma = urljoin(base, entry.url)
+ others = (urljoin(base, url) for url in (
+ reverse('home:index'),
+ reverse('entries:atom'),
+ reverse('entries:rss'),
+ ))
+ ping_hub(perma)
+ for url in others:
+ ping_hub(url)
+
+ res = HttpResponse(status=201)
+ res['Location'] = perma
+ return res