def to_html(entry): """:deprecated:""" if not entry: return None html = '<div class="h-cite">' foot = '</div>' if 'author' in entry: author_html = '<div class="p-author h-card">' author_foot = '</div>' if 'url' in entry['author']: author_html += '<a class="u-url" href="{}">'.format( entry['author']['url']) author_foot = '</a>' + author_foot if 'photo' in entry['author']: author_html += '<img src="{}" />'.format( entry['author']['photo']) if 'name' in entry['author']: author_html += '<span class="p-name">{}</span>'.format( entry['author']['name']) html += author_html + author_foot if 'name' in entry: html += '<h1 class="p-name">{}</h1>'.format(entry['name']) if 'content' in entry: html += '<div class="{}e-content">{}</div>'.format( 'p-name ' if 'name' not in entry else '', entry['content']) permalink = '' permalink_foot = '' if 'url' in entry: permalink += '<a class="u-url" href="{}">'.format(entry['url']) permalink_foot += '</a>' if 'published' in entry: published = entry['published'] try: pubdate = mf2util.parse_dt(published) pubiso = pubdate.isoformat() pubpretty = pubdate.strftime('%c') except: app.logger.warning('failed to parse datetime: ' + published, exc_info=True) pubiso = pubpretty = published permalink += '<time datetime="{}">{}</time>'.format(pubiso, pubpretty) else: permalink += 'link' html += permalink + permalink_foot return html + foot
def to_html(entry): """:deprecated:""" if not entry: return None html = '<div class="h-cite">' foot = '</div>' if 'author' in entry: author_html = '<div class="p-author h-card">' author_foot = '</div>' if 'url' in entry['author']: author_html += '<a class="u-url" href="{}">'.format( entry['author']['url']) author_foot = '</a>' + author_foot if 'photo' in entry['author']: author_html += '<img src="{}" />'.format(entry['author']['photo']) if 'name' in entry['author']: author_html += '<span class="p-name">{}</span>'.format( entry['author']['name']) html += author_html + author_foot if 'name' in entry: html += '<h1 class="p-name">{}</h1>'.format(entry['name']) if 'content' in entry: html += '<div class="{}e-content">{}</div>'.format( 'p-name ' if 'name' not in entry else '', entry['content']) permalink = '' permalink_foot = '' if 'url' in entry: permalink += '<a class="u-url" href="{}">'.format(entry['url']) permalink_foot += '</a>' if 'published' in entry: published = entry['published'] try: pubdate = mf2util.parse_dt(published) pubiso = pubdate.isoformat() pubpretty = pubdate.strftime('%c') except: app.logger.warning('failed to parse datetime: ' + published, exc_info=True) pubiso = pubpretty = published permalink += '<time datetime="{}">{}</time>'.format(pubiso, pubpretty) else: permalink += 'link' html += permalink + permalink_foot return html + foot
def save_post(post): was_draft = post.draft pub_str = request.form.get('published') if pub_str: post.published = mf2util.parse_dt(pub_str) start_str = request.form.get('start') if start_str: start = mf2util.parse_dt(start_str) if start: post.start = start post.start_utcoffset = start.utcoffset() end_str = request.form.get('end') if end_str: end = mf2util.parse_dt(end_str) if end: post.end = end post.end_utcoffset = end.utcoffset() now = datetime.datetime.utcnow() if not post.published or was_draft: post.published = now post.updated = now # populate the Post object and save it to the database, # redirect to the view post.title = request.form.get('title', '') post.content = request.form.get('content') post.draft = request.form.get('action') == 'save_draft' post.hidden = request.form.get('hidden', 'false') == 'true' venue_name = request.form.get('new_venue_name') venue_lat = request.form.get('new_venue_latitude') venue_lng = request.form.get('new_venue_longitude') if venue_name and venue_lat and venue_lng: venue = Venue() venue.name = venue_name venue.location = { 'latitude': float(venue_lat), 'longitude': float(venue_lng), } venue.update_slug('{}-{}'.format(venue_lat, venue_lng)) db.session.add(venue) db.session.commit() hooks.fire('venue-saved', venue, request.form) post.venue = venue else: venue_id = request.form.get('venue') if venue_id: post.venue = Venue.query.get(venue_id) lat = request.form.get('latitude') lon = request.form.get('longitude') if lat and lon: if post.location is None: post.location = {} post.location['latitude'] = float(lat) post.location['longitude'] = float(lon) loc_name = request.form.get('location_name') if loc_name is not None: post.location['name'] = loc_name else: post.location = None for url_attr, context_attr in (('in_reply_to', 'reply_contexts'), ('repost_of', 'repost_contexts'), ('like_of', 'like_contexts'), ('bookmark_of', 'bookmark_contexts')): url_str = request.form.get(url_attr) if url_str is not None: urls = util.multiline_string_to_list(url_str) setattr(post, url_attr, urls) # fetch contexts before generating a slug contexts.fetch_contexts(post) syndication = request.form.get('syndication') if syndication is not None: post.syndication = util.multiline_string_to_list(syndication) audience = request.form.get('audience') if audience is not None: post.audience = util.multiline_string_to_list(audience) tags = request.form.getlist('tags') if post.post_type != 'article' and post.content: # parse out hashtags as tag links from note-like posts tags += util.find_hashtags(post.content) tags = list(filter(None, map(util.normalize_tag, tags))) post.tags = [Tag.query.filter_by(name=tag).first() or Tag(tag) for tag in tags] slug = request.form.get('slug') if slug: post.slug = util.slugify(slug) elif not post.slug or was_draft: post.slug = post.generate_slug() # events should use their start date for permalinks path_date = post.start or post.published if post.draft: m = hashlib.md5() m.update(bytes(path_date.isoformat() + '|' + post.slug, 'utf-8')) post.path = 'drafts/{}'.format(m.hexdigest()) elif not post.path or was_draft: base_path = '{}/{:02d}/{}'.format( path_date.year, path_date.month, post.slug) # generate a unique path unique_path = base_path idx = 1 while Post.load_by_path(unique_path): unique_path = '{}-{}'.format(base_path, idx) idx += 1 post.path = unique_path # generate short path if not post.short_path: short_base = '{}/{}'.format( util.tag_for_post_type(post.post_type), util.base60_encode(util.date_to_ordinal(path_date))) short_paths = set( row[0] for row in db.session.query(Post.short_path).filter( Post.short_path.startswith(short_base)).all()) for idx in itertools.count(1): post.short_path = short_base + util.base60_encode(idx) if post.short_path not in short_paths: break infiles = request.files.getlist('files') + request.files.getlist('photo') current_app.logger.debug('infiles: %s', infiles) for infile in infiles: if infile and infile.filename: current_app.logger.debug('receiving uploaded file %s', infile) attachment = create_attachment_from_file(post, infile) os.makedirs(os.path.dirname(attachment.disk_path), exist_ok=True) infile.save(attachment.disk_path) post.attachments.append(attachment) # pre-render the post html html = util.markdown_filter(post.content, img_path=post.get_image_path()) html = util.autolink(html) if post.post_type == 'article': html = util.process_people_to_microcards(html) else: html = util.process_people_to_at_names(html) post.content_html = html if not post.id: db.session.add(post) db.session.commit() current_app.logger.debug('saved post %d %s', post.id, post.permalink) redirect_url = post.permalink hooks.fire('post-saved', post, request.form) return redirect(redirect_url)
def save_post(post): was_draft = post.draft pub_str = request.form.get('published') if pub_str: pub = mf2util.parse_dt(pub_str) if pub.tzinfo: pub = pub.astimezone(datetime.timezone.utc) pub = pub.replace(tzinfo=None) post.published = pub if not post.published or was_draft: post.published = datetime.datetime.utcnow() # populate the Post object and save it to the database, # redirect to the view post.title = request.form.get('title', '') post.content = request.form.get('content') post.draft = request.form.get('action') == 'save_draft' post.hidden = request.form.get('hidden', 'false') == 'true' venue_name = request.form.get('new_venue_name') venue_lat = request.form.get('new_venue_latitude') venue_lng = request.form.get('new_venue_longitude') if venue_name and venue_lat and venue_lng: venue = Venue() venue.name = venue_name venue.location = { 'latitude': float(venue_lat), 'longitude': float(venue_lng), } venue.update_slug('{}-{}'.format(venue_lat, venue_lng)) db.session.add(venue) db.session.commit() hooks.fire('venue-saved', venue, request.form) post.venue = venue else: venue_id = request.form.get('venue') if venue_id: post.venue = Venue.query.get(venue_id) lat = request.form.get('latitude') lon = request.form.get('longitude') if lat and lon: if post.location is None: post.location = {} post.location['latitude'] = float(lat) post.location['longitude'] = float(lon) loc_name = request.form.get('location_name') if loc_name is not None: post.location['name'] = loc_name else: post.location = None for url_attr, context_attr in (('in_reply_to', 'reply_contexts'), ('repost_of', 'repost_contexts'), ('like_of', 'like_contexts'), ('bookmark_of', 'bookmark_contexts')): url_str = request.form.get(url_attr) if url_str is not None: urls = util.multiline_string_to_list(url_str) setattr(post, url_attr, urls) # fetch contexts before generating a slug contexts.fetch_contexts(post) syndication = request.form.get('syndication') if syndication is not None: post.syndication = util.multiline_string_to_list(syndication) audience = request.form.get('audience') if audience is not None: post.audience = util.multiline_string_to_list(audience) tags = request.form.get('tags', '').split(',') tags = list(filter(None, map(util.normalize_tag, tags))) post.tags = [Tag.query.filter_by(name=tag).first() or Tag(tag) for tag in tags] slug = request.form.get('slug') if slug: post.slug = util.slugify(slug) elif not post.slug or was_draft: post.slug = post.generate_slug() if post.draft: m = hashlib.md5() m.update(bytes(post.published.isoformat() + '|' + post.slug, 'utf-8')) post.path = 'drafts/{}'.format(m.hexdigest()) elif not post.path or was_draft: base_path = '{}/{:02d}/{}'.format( post.published.year, post.published.month, post.slug) # generate a unique path unique_path = base_path idx = 1 while Post.load_by_path(unique_path): unique_path = '{}-{}'.format(base_path, idx) idx += 1 post.path = unique_path # TODO accept multiple photos and captions inphoto = request.files.get('photo') if inphoto and inphoto.filename: app.logger.debug('receiving uploaded file %s', inphoto) relpath, photo_url, fullpath \ = generate_upload_path(post, inphoto) if not os.path.exists(os.path.dirname(fullpath)): os.makedirs(os.path.dirname(fullpath)) app.logger.debug('uploading photo to %s', fullpath) inphoto.save(fullpath) caption = request.form.get('caption') post.photos = [{ 'filename': os.path.basename(relpath), 'caption': caption, }] file_to_url = {} infiles = request.files.getlist('files') app.logger.debug('infiles: %s', infiles) for infile in infiles: if infile and infile.filename: app.logger.debug('receiving uploaded file %s', infile) relpath, photo_url, fullpath \ = generate_upload_path(post, infile) if not os.path.exists(os.path.dirname(fullpath)): os.makedirs(os.path.dirname(fullpath)) infile.save(fullpath) file_to_url[infile] = photo_url app.logger.debug('uploaded files map %s', file_to_url) # pre-render the post html post.content_html = util.markdown_filter( post.content, img_path=post.get_image_path()) if not post.id: db.session.add(post) db.session.commit() app.logger.debug('saved post %d %s', post.id, post.permalink) redirect_url = post.permalink hooks.fire('post-saved', post, request.form) return redirect(redirect_url)
def micropub_endpoint(): app.logger.info( "received micropub request %s, args=%s, form=%s, headers=%s", request, request.args, request.form, request.headers) bearer_prefix = 'Bearer ' header_token = request.headers.get('authorization') if header_token and header_token.startswith(bearer_prefix): token = header_token[len(bearer_prefix):] else: token = request.form.get('access_token') if not token: app.logger.warn('hit micropub endpoint with no access token') abort(401) try: decoded = jwt.decode(token, app.config['SECRET_KEY']) except jwt.DecodeError as e: app.logger.warn('could not decode access token: %s', e) abort(401) me = decoded.get('me') parsed = urllib.parse.urlparse(me) user = auth.load_user(parsed.netloc) if not user: app.logger.warn('received valid access token for invalid user: %s', me) abort(401) app.logger.debug('successfully authenticated as user %s => %s', me, user) in_reply_to = request.form.get('in-reply-to') like_of = request.form.get('like-of') post = Post('reply' if in_reply_to else 'like' if like_of else 'note') post._writeable = True post.title = request.form.get('name') post.content = request.form.get('content') if in_reply_to: post.in_reply_to.append(in_reply_to) if like_of: post.like_of.append(like_of) pub_str = request.form.get('published') if pub_str: pub = mf2util.parse_dt(pub_str) if pub.tzinfo: pub = pub.astimezone(datetime.timezone.utc) pub = pub.replace(tzinfo=None) post.pub_date = pub else: post.pub_date = datetime.datetime.utcnow() post.reserve_date_index() loc_str = request.form.get('location') geo_prefix = 'geo:' if loc_str and loc_str.startswith(geo_prefix): loc_str = loc_str[len(geo_prefix):] loc_params = loc_str.split(';') if loc_params: lat, lon = loc_params[0].split(',', 1) if lat and lon: post.location = Location(latitude=float(lat), longitude=float(lon), name=request.form.get('place_name')) synd_url = request.form.get('syndication') if synd_url: post.syndication.append(synd_url) photo_file = request.files.get('photo') if photo_file: relpath, photo_url, fullpath \ = generate_upload_path(post, photo_file, '.jpg') if not os.path.exists(os.path.dirname(fullpath)): os.makedirs(os.path.dirname(fullpath)) photo_file.save(fullpath) content = '![]({})'.format(os.path.basename(photo_url)) if post.content: content += '\n\n' + post.content post.content = content post.save() post._writeable = False with Metadata.writeable() as mdata: mdata.add_or_update_post(post) mdata.save() return make_response('', 201, {'Location': post.permalink})
def save_post(post): was_draft = post.draft pub_str = request.form.get('published') if pub_str: post.published = mf2util.parse_dt(pub_str) if post.published.tzinfo: post.published = post.published.astimezone(datetime.timezone.utc)\ .replace(tzinfo=None) if 'post_type' in request.form: post.post_type = request.form.get('post_type') start_str = request.form.get('start') if start_str: start = mf2util.parse_dt(start_str) if start: post.start = start post.start_utcoffset = start.utcoffset() end_str = request.form.get('end') if end_str: end = mf2util.parse_dt(end_str) if end: post.end = end post.end_utcoffset = end.utcoffset() now = datetime.datetime.utcnow() if not post.published or was_draft: post.published = now post.updated = now # populate the Post object and save it to the database, # redirect to the view post.title = request.form.get('title', '') post.content = request.form.get('content') post.draft = request.form.get('action') == 'save_draft' post.hidden = request.form.get('hidden', 'false') == 'true' post.friends_only = request.form.get('friends_only', 'false') == 'true' venue_name = request.form.get('new_venue_name') venue_lat = request.form.get('new_venue_latitude') venue_lng = request.form.get('new_venue_longitude') if venue_name and venue_lat and venue_lng: venue = Venue() venue.name = venue_name venue.location = { 'latitude': float(venue_lat), 'longitude': float(venue_lng), } venue.update_slug('{}-{}'.format(venue_lat, venue_lng)) db.session.add(venue) db.session.commit() hooks.fire('venue-saved', venue, request.form) post.venue = venue else: venue_id = request.form.get('venue') if venue_id: post.venue = Venue.query.get(venue_id) lat = request.form.get('latitude') lon = request.form.get('longitude') if lat and lon: if post.location is None: post.location = {} post.location['latitude'] = float(lat) post.location['longitude'] = float(lon) loc_name = request.form.get('location_name') if loc_name is not None: post.location['name'] = loc_name else: post.location = None for url_attr, context_attr in (('in_reply_to', 'reply_contexts'), ('repost_of', 'repost_contexts'), ('like_of', 'like_contexts'), ('bookmark_of', 'bookmark_contexts')): url_str = request.form.get(url_attr) if url_str is not None: urls = util.multiline_string_to_list(url_str) setattr(post, url_attr, urls) # fetch contexts before generating a slug contexts.fetch_contexts(post) if 'item-name' in request.form: post.item = util.trim_nulls({ 'name': request.form.get('item-name'), 'author': request.form.get('item-author'), 'photo': request.form.get('item-photo'), }) if 'rating' in request.form: rating = request.form.get('rating') post.rating = int(rating) if rating else None syndication = request.form.get('syndication') if syndication is not None: post.syndication = util.multiline_string_to_list(syndication) audience = request.form.get('audience') if audience is not None: post.audience = util.multiline_string_to_list(audience) tags = request.form.getlist('tags') if post.post_type != 'article' and post.content: # parse out hashtags as tag links from note-like posts tags += util.find_hashtags(post.content) tags = list(filter(None, map(util.normalize_tag, tags))) post.tags = [Tag.query.filter_by(name=tag).first() or Tag(tag) for tag in tags] post.people = [] people = request.form.getlist('people') for person in people: nick = Nick.query.filter_by(name=person).first() if nick: post.people.append(nick.contact) slug = request.form.get('slug') if slug: post.slug = util.slugify(slug) elif not post.slug or was_draft: post.slug = post.generate_slug() # events should use their start date for permalinks path_date = post.start or post.published if post.draft: m = hashlib.md5() m.update(bytes(path_date.isoformat() + '|' + post.slug, 'utf-8')) post.path = 'drafts/{}'.format(m.hexdigest()) elif not post.path or was_draft: base_path = '{}/{:02d}/{}'.format( path_date.year, path_date.month, post.slug) # generate a unique path unique_path = base_path idx = 1 while Post.load_by_path(unique_path): unique_path = '{}-{}'.format(base_path, idx) idx += 1 post.path = unique_path # generate short path if not post.short_path: short_base = '{}/{}'.format( util.tag_for_post_type(post.post_type), util.base60_encode(util.date_to_ordinal(path_date))) short_paths = set( row[0] for row in db.session.query(Post.short_path).filter( Post.short_path.startswith(short_base)).all()) for idx in itertools.count(1): post.short_path = short_base + util.base60_encode(idx) if post.short_path not in short_paths: break infiles = request.files.getlist('files') + request.files.getlist('photo') current_app.logger.debug('infiles: %s', infiles) for infile in infiles: if infile and infile.filename: current_app.logger.debug('receiving uploaded file %s', infile) attachment = create_attachment_from_file(post, infile) os.makedirs(os.path.dirname(attachment.disk_path), exist_ok=True) infile.save(attachment.disk_path) post.attachments.append(attachment) photo_url = request.form.get('photo') if photo_url: current_app.logger.debug('downloading photo from url %s', photo_url) temp_filename, headers = urllib.request.urlretrieve(photo_url) content_type = headers.get('content-type', '') mimetype = content_type and content_type.split(';')[0].strip() filename = os.path.basename(urllib.parse.urlparse(photo_url).path) attachment = create_attachment(post, filename, mimetype) os.makedirs(os.path.dirname(attachment.disk_path), exist_ok=True) shutil.copyfile(temp_filename, attachment.disk_path) urllib.request.urlcleanup() post.attachments.append(attachment) # pre-render the post html html = util.markdown_filter(post.content, img_path=post.get_image_path()) html = util.autolink(html) if post.post_type == 'article': html = util.process_people_to_microcards(html) else: html = util.process_people_to_at_names(html) post.content_html = html if not post.id: db.session.add(post) db.session.commit() current_app.logger.debug('saved post %d %s', post.id, post.permalink) redirect_url = post.permalink hooks.fire('post-saved', post, request.form) return redirect(redirect_url)