Mercurial > piecrust2
comparison piecrust/admin/views/micropub.py @ 895:accfe8fc8440
admin: Add a Micropub endpoint.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 02 Jul 2017 22:23:12 -0700 |
parents | |
children | d65838abbd90 |
comparison
equal
deleted
inserted
replaced
894:ca357249a321 | 895:accfe8fc8440 |
---|---|
1 import re | |
2 import os | |
3 import os.path | |
4 import logging | |
5 import datetime | |
6 from werkzeug.utils import secure_filename | |
7 from flask import g, request, abort, Response | |
8 from flask_indieauth import requires_indieauth | |
9 from ..blueprint import foodtruck_bp | |
10 from piecrust.page import Page | |
11 | |
12 | |
13 logger = logging.getLogger(__name__) | |
14 | |
15 re_unsafe_asset_char = re.compile('[^a-zA-Z0-9_]') | |
16 | |
17 | |
18 @foodtruck_bp.route('/micropub', methods=['POST']) | |
19 @requires_indieauth | |
20 def micropub(): | |
21 post_type = request.form.get('h') | |
22 | |
23 if post_type == 'entry': | |
24 uri = _create_hentry() | |
25 _run_publisher() | |
26 return _get_location_response(uri) | |
27 | |
28 logger.debug("Unknown or unsupported update type.") | |
29 logger.debug(request.form) | |
30 abort(400) | |
31 | |
32 | |
33 def _run_publisher(): | |
34 pcapp = g.site.piecrust_app | |
35 target = pcapp.config.get('micropub/publish_target', 'default') | |
36 logger.debug("Running pushing target '%s'." % target) | |
37 g.site.publish(target) | |
38 | |
39 | |
40 def _get_location_response(uri): | |
41 logger.debug("Redirecting to: %s" % uri) | |
42 r = Response() | |
43 r.status_code = 201 | |
44 r.headers.add('Location', uri) | |
45 return r | |
46 | |
47 | |
48 def _create_hentry(): | |
49 f = request.form | |
50 summary = f.get('summary') | |
51 categories = f.getlist('category[]') | |
52 location = f.get('location') | |
53 reply_to = f.get('in-reply-to') | |
54 status = f.get('post-status') | |
55 # pubdate = f.get('published', 'now') | |
56 | |
57 # Figure out the title of the post. | |
58 name = f.get('name') | |
59 if not name: | |
60 name = f.get('name[]') | |
61 | |
62 # Figure out the contents of the post. | |
63 post_format = None | |
64 content = f.get('content') | |
65 if not content: | |
66 content = f.get('content[]') | |
67 if not content: | |
68 content = f.get('content[html]') | |
69 post_format = 'none' | |
70 | |
71 if not content: | |
72 logger.error("No content specified!") | |
73 logger.error(dict(request.form)) | |
74 abort(400) | |
75 | |
76 # TODO: setting to conserve Windows-type line endings? | |
77 content = content.replace('\r\n', '\n') | |
78 if summary: | |
79 summary = summary.replace('\r\n', '\n') | |
80 | |
81 # Figure out the slug of the post. | |
82 now = datetime.datetime.now() | |
83 slug = f.get('slug') | |
84 if not slug: | |
85 slug = f.get('mp-slug') | |
86 if not slug: | |
87 slug = '%02d%02d%02d' % (now.hour, now.minute, now.second) | |
88 | |
89 # Get the media to attach to the post. | |
90 photo_urls = None | |
91 if 'photo' in f: | |
92 photo_urls = [f['photo']] | |
93 elif 'photo[]' in f: | |
94 photo_urls = f.getlist('photo[]') | |
95 | |
96 photos = None | |
97 if 'photo' in request.files: | |
98 photos = [request.files['photo']] | |
99 elif 'photo[]' in request.files: | |
100 photos = request.files.getlist('photo[]') | |
101 | |
102 # Create the post in the correct content source. | |
103 pcapp = g.site.piecrust_app | |
104 source_name = pcapp.config.get('micropub/source', 'posts') | |
105 source = pcapp.getSource(source_name) | |
106 | |
107 metadata = { | |
108 'date': now, | |
109 'slug': slug | |
110 } | |
111 logger.debug("Creating item with metadata: %s" % metadata) | |
112 content_item = source.createContent(metadata) | |
113 if content_item is None: | |
114 logger.error("Can't create item for: %s" % metadata) | |
115 abort(500) | |
116 | |
117 # TODO: add proper APIs for creating related assets. | |
118 photo_names = None | |
119 if photos: | |
120 photo_dir, _ = os.path.splitext(content_item.spec) | |
121 photo_dir += '-assets' | |
122 if not os.path.exists(photo_dir): | |
123 os.makedirs(photo_dir) | |
124 | |
125 photo_names = [] | |
126 for photo in photos: | |
127 if not photo or not photo.filename: | |
128 logger.warning("Got empty photo in request files... skipping.") | |
129 continue | |
130 | |
131 fn = secure_filename(photo.filename) | |
132 fn = re_unsafe_asset_char.sub('_', fn) | |
133 photo_path = os.path.join(photo_dir, fn) | |
134 logger.info("Uploading file to: %s" % photo_path) | |
135 photo.save(photo_path) | |
136 | |
137 fn_no_ext, _ = os.path.splitext(fn) | |
138 photo_names.append(fn_no_ext) | |
139 | |
140 logger.debug("Writing to item: %s" % content_item.spec) | |
141 with source.openItem(content_item, mode='w') as fp: | |
142 fp.write('---\n') | |
143 if name: | |
144 fp.write('title: "%s"\n' % name) | |
145 if categories: | |
146 fp.write('tags: [%s]\n' % ','.join(categories)) | |
147 if location: | |
148 fp.write('location: %s\n' % location) | |
149 if reply_to: | |
150 fp.write('reply_to: "%s"\n' % reply_to) | |
151 if status: | |
152 fp.write('status: %s\n' % status) | |
153 if post_format: | |
154 fp.write('format: %s\n' % post_format) | |
155 fp.write('time: %02d:%02d:%02d\n' % (now.hour, now.minute, now.second)) | |
156 fp.write('---\n') | |
157 | |
158 if summary: | |
159 fp.write(summary) | |
160 fp.write('\n') | |
161 fp.write('<!--break-->\n\n') | |
162 fp.write(content) | |
163 | |
164 if photo_urls: | |
165 fp.write('\n\n') | |
166 for pu in photo_urls: | |
167 fp.write('<img src="{{assets.%s}}" alt=""/>\n\n' % pu) | |
168 | |
169 if photo_names: | |
170 fp.write('\n\n') | |
171 for pn in photo_names: | |
172 fp.write('<img src="{{assets.%s}}" alt="%s"/>\n\n' % | |
173 (pn, pn)) | |
174 | |
175 route = pcapp.getSourceRoute(source.name) | |
176 if route is None: | |
177 logger.error("Can't find route for source: %s" % source.name) | |
178 abort(500) | |
179 | |
180 page = Page(source, content_item) | |
181 uri = page.getUri() | |
182 return uri | |
183 |