def test_send_wms(client, mocker): getter = mocker.patch('requests.get') poster = mocker.patch('requests.post') urlopen = mocker.patch('urllib.request.urlopen') post = Post('note') post.content = 'This note links to [wikipedia](https://en.wikipedia.org/wiki/Webmention)' post.content_html = 'This note links to <a href="https://en.wikipedia.org/wiki/Webmention">wikipedia</a>' post.path = '2014/11/wm-sender-test' db.session.add(post) db.session.commit() urlopen.return_value = FakeUrlOpen( info=FakeUrlMetadata(content_type='text/html', content_length=256)) getter.return_value = FakeResponse(text="""<!DOCTYPE html> <html> <link rel="webmention" href="https://en.wikipedia.org/endpoint"> </html>""") wm_sender.do_send_webmentions(post.id) getter.assert_called_with('https://en.wikipedia.org/wiki/Webmention') poster.assert_called_with('https://en.wikipedia.org/endpoint', data={ 'source': post.permalink, 'target': 'https://en.wikipedia.org/wiki/Webmention', }, headers={ 'content-type': 'application/x-www-form-urlencoded', 'accept': 'application/json', })
def share_on_facebook(): from .twitter import collect_images if request.method == "GET": post = Post.load_by_id(request.args.get("id")) message, link, name, picture = guess_content(post) imgs = [urljoin(get_settings().site_url, img) for img in collect_images(post)] albums = [] if imgs: current_app.logger.debug("fetching user albums") resp = requests.get( "https://graph.facebook.com/v2.2/me/albums", params={"access_token": get_settings().facebook_access_token}, ) resp.raise_for_status() current_app.logger.debug("user albums response %s: %s", resp, resp.text) albums = resp.json().get("data", []) return render_template( "admin/share_on_facebook.jinja2", post=post, preview=message, link=link, name=name, picture=picture, imgs=imgs, albums=albums, ) try: post_id = request.form.get("post_id") preview = request.form.get("preview") img_url = request.form.get("img") is_photo = request.form.get("post_type") == "photo" album_id = request.form.get("album") link = request.form.get("link") if album_id == "new": album_id = create_album(request.form.get("new_album_name"), request.form.get("new_album_message")) post = Post.load_by_id(post_id) facebook_url = handle_new_or_edit( post, message=preview, link=link, name=None, picture=img_url, is_photo=is_photo, album_id=album_id ) db.session.commit() if has_request_context(): flash( 'Shared on Facebook: <a href="{}">Original</a>, ' '<a href="{}">On Facebook</a><br/>'.format(post.permalink, facebook_url) ) return redirect(post.permalink) except Exception as e: if has_request_context(): current_app.logger.exception("posting to facebook") flash("Share on Facebook Failed! Exception: {}".format(e)) return redirect(url_for("views.index"))
def source_post(app, db): post = Post('note') post.content = 'This note links to [wikipedia](https://en.wikipedia.org/wiki/Webmention)' post.content_html = 'This note links to <a href="https://en.wikipedia.org/wiki/Webmention">wikipedia</a>' post.path = '2014/11/wm-sender-test' db.session.add(post) db.session.commit() return post
def share_on_facebook(): from .twitter import collect_images if request.method == 'GET': post = Post.load_by_id(request.args.get('id')) message, link, name, picture = guess_content(post) imgs = [urljoin(get_settings().site_url, img) for img in collect_images(post)] albums = [] if imgs: current_app.logger.debug('fetching user albums') resp = requests.get( 'https://graph.facebook.com/v2.2/me/albums', params={'access_token': get_settings().facebook_access_token}) resp.raise_for_status() current_app.logger.debug( 'user albums response %s: %s', resp, resp.text) albums = resp.json().get('data', []) return render_template('admin/share_on_facebook.jinja2', post=post, preview=message, link=link, name=name, picture=picture, imgs=imgs, albums=albums) try: post_id = request.form.get('post_id') preview = request.form.get('preview') img_url = request.form.get('img') is_photo = request.form.get('post_type') == 'photo' album_id = request.form.get('album') link = request.form.get('link') if album_id == 'new': album_id = create_album( request.form.get('new_album_name'), request.form.get('new_album_message')) post = Post.load_by_id(post_id) facebook_url = handle_new_or_edit( post, message=preview, link=link, name=None, picture=img_url, is_photo=is_photo, album_id=album_id) db.session.commit() if has_request_context(): flash('Shared on Facebook: <a href="{}">Original</a>, ' '<a href="{}">On Facebook</a><br/>' .format(post.permalink, facebook_url)) return redirect(post.permalink) except Exception as e: if has_request_context(): current_app.logger.exception('posting to facebook') flash('Share on Facebook Failed! Exception: {}'.format(e)) return redirect(url_for('views.index'))
def share_on_facebook(): from .twitter import collect_images if request.method == 'GET': post = Post.load_by_id(request.args.get('id')) message, link, name, picture = guess_content(post) imgs = [urllib.parse.urljoin(get_settings().site_url, img) for img in collect_images(post)] albums = [] if imgs: current_app.logger.debug('fetching user albums') resp = requests.get( 'https://graph.facebook.com/v2.2/me/albums', params={'access_token': get_settings().facebook_access_token}) resp.raise_for_status() current_app.logger.debug( 'user albums response %s: %s', resp, resp.text) albums = resp.json().get('data', []) return render_template('admin/share_on_facebook.jinja2', post=post, preview=message, link=link, name=name, picture=picture, imgs=imgs, albums=albums) try: post_id = request.form.get('post_id') preview = request.form.get('preview') img_url = request.form.get('img') is_photo = request.form.get('post_type') == 'photo' album_id = request.form.get('album') link = request.form.get('link') if album_id == 'new': album_id = create_album( request.form.get('new_album_name'), request.form.get('new_album_message')) post = Post.load_by_id(post_id) facebook_url = handle_new_or_edit( post, message=preview, link=link, name=None, picture=img_url, is_photo=is_photo, album_id=album_id) db.session.commit() if has_request_context(): flash('Shared on Facebook: <a href="{}">Original</a>, ' '<a href="{}">On Facebook</a><br/>' .format(post.permalink, facebook_url)) return redirect(post.permalink) except Exception as e: if has_request_context(): current_app.logger.exception('posting to facebook') flash('Share on Facebook Failed! Exception: {}'.format(e)) return redirect(url_for('views.index'))
def edit_by_id(): id = request.args.get('id') if not id: abort(404) post = Post.load_by_id(id) if not post: abort(404) type = 'post' if not request.args.get('advanced') and post.post_type: type = post.post_type if post.draft: button_text = { 'publish': 'Publish Draft', 'publish_quietly': 'Publish Draft Quietly', 'publish+tweet': 'Publish Draft & Tweet', 'save_draft': 'Resave Draft', } else: button_text = { 'publish': 'Republish', 'publish_quietly': 'Republish Quietly', 'publish+tweet': 'Republish & Tweet', 'save_draft': 'Unpublish, Save as Draft', } template = 'admin/edit_' + type + '.jinja2' if request.args.get('full'): template = 'admin/edit_post_all.jinja2' venues = Venue.query.order_by(Venue.name).all() return render_template(template, edit_type='edit', post=post, tags=get_tags(), top_tags=get_top_tags(20), people=get_contact_nicks(), button_text=button_text, venues=venues)
def do_send_to_twitter(post_id, app_config): with async_app_context(app_config): current_app.logger.debug('auto-posting to twitter for %s', post_id) post = Post.load_by_id(post_id) in_reply_to, repost_of, like_of = util.posse_post_discovery( post, PERMALINK_RE) # cowardly refuse to auto-POSSE a reply/repost/like when the # target tweet is not found. if post.in_reply_to and not in_reply_to: current_app.logger.warn('could not find tweet to reply to for %s', post.in_reply_to) return None elif post.repost_of and not repost_of: current_app.logger.warn('could not find tweet to repost for %s', post.repost_of) preview, img_url = guess_raw_share_tweet_content(post) elif post.like_of and not like_of: current_app.logger.warn('could not find tweet to like for %s', post.like_of) return None else: preview, img_url = guess_tweet_content(post, in_reply_to) response = do_tweet(post_id, preview, img_url, in_reply_to, repost_of, like_of) return str(response)
def do_send_to_twitter(post_id, app_config): with async_app_context(app_config): current_app.logger.debug('auto-posting to twitter for %s', post_id) post = Post.load_by_id(post_id) in_reply_to, repost_of, like_of = util.posse_post_discovery( post, PERMALINK_RE) # cowardly refuse to auto-POSSE a reply/repost/like when the # target tweet is not found. if post.in_reply_to and not in_reply_to: current_app.logger.warn( 'could not find tweet to reply to for %s', post.in_reply_to) return None elif post.repost_of and not repost_of: current_app.logger.warn( 'could not find tweet to repost for %s', post.repost_of) preview, img_url = guess_raw_share_tweet_content(post) elif post.like_of and not like_of: current_app.logger.warn( 'could not find tweet to like for %s', post.like_of) return None else: preview, img_url = guess_tweet_content(post, in_reply_to) response = do_tweet(post_id, preview, img_url, in_reply_to, repost_of, like_of) return str(response)
def post_associated_file_by_historic_path(post_type, year, month, day, index, filename): post = Post.load_by_historic_path('{}/{}/{:02d}/{:02d}/{}'.format( post_type, year, month, day, index)) if not post: abort(404) return redirect('/{}/files/{}'.format(post.path, filename))
def do_send_to_wordpress(post_id, app_config): with async_app_context(app_config): post = Post.load_by_id(post_id) if post.like_of: for url in post.like_of: try_post_like(url, post) elif post.in_reply_to: for url in post.in_reply_to: try_post_reply(url, post)
def do_reverse_geocode_post(postid, app_config): with async_app_context(app_config): post = Post.load_by_id(postid) if post.location and 'latitude' in post.location \ and 'longitude' in post.location: adr = do_reverse_geocode(post.location['latitude'], post.location['longitude']) # copy the dict so that the ORM recognizes # that it changed post.location = dict(post.location) post.location.update(adr) db.session.commit()
def delete_by_id(): id = request.args.get('id') post = Post.load_by_id(id) if not post: abort(404) post.deleted = True db.session.commit() hooks.fire('post-deleted', post, request.args) redirect_url = request.args.get('redirect') or url_for('views.index') current_app.logger.debug('redirecting to {}'.format(redirect_url)) return redirect(redirect_url)
def do_reverse_geocode_post(postid, app_config): with async_app_context(app_config): post = Post.load_by_id(postid) if not post: return if post.location and 'latitude' in post.location \ and 'longitude' in post.location: adr = do_reverse_geocode(post.location['latitude'], post.location['longitude']) # copy the dict so that the ORM recognizes # that it changed post.location = dict(post.location) post.location.update(adr) db.session.commit()
def do_send_to_facebook(post_id, app_config): with async_app_context(app_config): current_app.logger.debug("auto-posting to facebook for %s", post_id) post = Post.load_by_id(post_id) message, link, name, picture = guess_content(post) facebook_url = handle_new_or_edit(post, message, link, name, picture, post.post_type == "photo", album_id=None) db.session.commit() if has_request_context(): flash( 'Shared on Facebook: <a href="{}">Original</a>, ' '<a href="{}">On Facebook</a><br/>'.format(post.permalink, facebook_url) ) return redirect(post.permalink)
def do_send_to_facebook(post_id, app_config): with async_app_context(app_config): current_app.logger.debug('auto-posting to facebook for %s', post_id) post = Post.load_by_id(post_id) message, link, name, picture = guess_content(post) facebook_url = handle_new_or_edit(post, message, link, name, picture, post.post_type == 'photo', album_id=None) db.session.commit() if has_request_context(): flash('Shared on Facebook: <a href="{}">Original</a>, ' '<a href="{}">On Facebook</a><br/>' .format(post.permalink, facebook_url)) return redirect(post.permalink)
def do_tweet(post_id, preview, img_url, in_reply_to, repost_of, like_of): try: post = Post.load_by_id(post_id) twitter_url = handle_new_or_edit(post, preview, img_url, in_reply_to, repost_of, like_of) db.session.commit() if has_request_context(): flash('Shared on Twitter: <a href="{}">Original</a>, ' '<a href="{}">On Twitter</a>'.format(post.permalink, twitter_url)) return redirect(post.permalink) except Exception as e: current_app.logger.exception('posting to twitter') if has_request_context(): flash('Share on Twitter Failed!. Exception: {}'.format(e)) return redirect(url_for('views.index'))
def new_post(type): if type not in util.POST_TYPES: abort(404) post = Post(type) post.published = post.updated = datetime.datetime.utcnow() post.content = '' if type == 'reply': in_reply_to = request.args.get('url') if in_reply_to: post.in_reply_to = [in_reply_to] elif type == 'share': repost_of = request.args.get('url') if repost_of: post.repost_of = [repost_of] elif type == 'like': like_of = request.args.get('url') if like_of: post.like_of = [like_of] elif type == 'bookmark': bookmark_of = request.args.get('url') if bookmark_of: post.bookmark_of = [bookmark_of] post.content = request.args.get('content') button_text = { 'publish': 'Publish', 'publish_quietly': 'Publish Quietly', 'publish+tweet': 'Publish & Tweet', 'save_draft': 'Save as Draft', } venues = Venue.query.order_by(Venue.name).all() return render_template('admin/edit_' + type + '.jinja2', edit_type='new', post=post, tags=get_tags(), top_tags=get_top_tags(20), people=get_contact_nicks(), button_text=button_text, venues=venues)
def share_on_twitter(): if request.method == 'GET': id = request.args.get('id') if not id: abort(404) post = Post.load_by_id(id) if not post: abort(404) current_app.logger.debug('sharing on twitter. post: %s', post) in_reply_to, repost_of, like_of \ = util.posse_post_discovery(post, PERMALINK_RE) current_app.logger.debug( 'discovered in-reply-to: %s, repost-of: %s, like-of: %s', in_reply_to, repost_of, like_of) if post.repost_of and not repost_of: preview, _ = guess_raw_share_tweet_content(post) imgs = list(collect_images(post.repost_contexts[0])) else: preview, _ = guess_tweet_content(post, in_reply_to) imgs = list(collect_images(post)) current_app.logger.debug('twitter post has images: %s', imgs) return render_template('admin/share_on_twitter.jinja2', preview=preview, post=post, in_reply_to=in_reply_to, repost_of=repost_of, like_of=like_of, imgs=imgs) post_id = request.form.get('post_id') preview = request.form.get('preview') img_url = request.form.get('img') in_reply_to = request.form.get('in_reply_to') repost_of = request.form.get('repost_of') like_of = request.form.get('like_of') return do_tweet(post_id, preview, img_url, in_reply_to, repost_of, like_of)
def new_post(type): post = Post(type) post.published = post.updated = datetime.datetime.utcnow() post.content = '' if type == 'reply': in_reply_to = request.args.get('url') if in_reply_to: post.in_reply_to = [in_reply_to] # post.reply_contexts = [contexts.create_context(in_reply_to)] elif type == 'share': repost_of = request.args.get('url') if repost_of: post.repost_of = [repost_of] # post.repost_contexts = [contexts.create_context(repost_of)] elif type == 'like': like_of = request.args.get('url') if like_of: post.like_of = [like_of] # post.like_contexts = [contexts.create_context(like_of)] elif type == 'bookmark': bookmark_of = request.args.get('url') if bookmark_of: post.bookmark_of = [bookmark_of] # post.bookmark_contexts = [contexts.create_context(bookmark_of)] post.content = request.args.get('content') button_text = { 'publish': 'Publish', 'publish_quietly': 'Publish Quietly', 'publish+tweet': 'Publish & Tweet', 'save_draft': 'Save as Draft', } if type == 'event': venues = Venue.query.order_by(Venue.name).all() else: venues = [] return render_template('admin/edit_' + type + '.jinja2', edit_type='new', post=post, tags=get_tags(), top_tags=get_top_tags(20), button_text=button_text, venues=venues)
def do_tweet(post_id, preview, img_url, in_reply_to, repost_of, like_of): try: post = Post.load_by_id(post_id) twitter_url = handle_new_or_edit( post, preview, img_url, in_reply_to, repost_of, like_of) db.session.commit() if has_request_context(): flash('Shared on Twitter: <a href="{}">Original</a>, ' '<a href="{}">On Twitter</a>' .format(post.permalink, twitter_url)) return redirect(post.permalink) except Exception as e: current_app.logger.exception('posting to twitter') if has_request_context(): flash('Share on Twitter Failed!. Exception: {}'.format(e)) return redirect(url_for('views.index'))
def send_webmentions_manually(): id = request.args.get('id') post = Post.load_by_id(id) return jsonify({ 'mentions': handle_new_or_edit(post), })
def find_target_post(target_url): current_app.logger.debug("looking for target post at %s", target_url) # follow redirects if necessary redirect_url = urllib.request.urlopen(target_url).geturl() if redirect_url and redirect_url != target_url: current_app.logger.debug("followed redirection to %s", redirect_url) target_url = redirect_url parsed_url = urllib.parse.urlparse(target_url) if not parsed_url: current_app.logger.warn("Could not parse target_url of received webmention: %s", target_url) return None try: # FIXME this is a less-than-perfect fix for hosting from a # subdirectory. The url_map may have some clever work-around. parsed_site_root = urllib.parse.urlparse(get_settings().site_url) site_prefix = parsed_site_root.path if site_prefix.endswith("/"): site_prefix = site_prefix[:-1] if not parsed_url.path.startswith(parsed_site_root.path): raise NotFound urls = current_app.url_map.bind(get_settings().site_url) path = parsed_url.path[len(site_prefix) :] current_app.logger.debug("target path with no prefix %s", path) endpoint, args = urls.match(path) current_app.logger.debug("found match for target url %r: %r", endpoint, args) except NotFound: current_app.logger.warn("Webmention could not find target for %s", parsed_url.path) return None post = None if endpoint == "views.post_by_path": year = args.get("year") month = args.get("month") slug = args.get("slug") post = Post.load_by_path("{}/{:02d}/{}".format(year, month, slug)) elif endpoint == "views.post_by_date": post_type = args.get("post_type") year = args.get("year") month = args.get("month") day = args.get("day") index = args.get("index") post = Post.load_by_date(post_type, year, month, day, index) elif endpoint == "views.post_by_old_date": post_type = args.get("post_type") yymmdd = args.get("yymmdd") year = int("20" + yymmdd[0:2]) month = int(yymmdd[2:4]) day = int(yymmdd[4:6]) post = Post.load_by_date(post_type, year, month, day, index) elif endpoint == "views.post_by_id": dbid = args.get("dbid") post = Post.load_by_id(dbid) if not post: current_app.logger.warn("Webmention target points to unknown post: {}".format(args)), return post
def post_by_short_path(tag, tail): post = Post.load_by_short_path('{}/{}'.format(tag, tail)) if not post: abort(404) return redirect(post.permalink)
def post_attachment(year, month, slug, filename): post = Post.load_by_path('{}/{:02d}/{}'.format(year, month, slug)) return render_attachment(post, filename)
def post_by_path(year, month, slug): post = Post.load_by_path('{}/{:02d}/{}'.format(year, month, slug)) return render_post(post)
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)
import os import json from redwind import app from redwind.models import Post if __name__ == '__main__': obj = {} for post in Post.iterate_all(): if post.facebook_url: obj[post.facebook_url] = post.path if post.twitter_url: obj[post.twitter_url] = post.path with open(os.path.join(app.root_path, '_data/syndication_index'), 'w') as f: json.dump(obj, f, indent=True)
def post_by_date(post_type, year, month, day, index, slug): post = Post.load_by_historic_path('{}/{}/{:02d}/{:02d}/{}'.format( post_type, year, month, day, index)) if not post: abort(404) return redirect(post.permalink)
def find_target_post(target_url): current_app.logger.debug("looking for target post at %s", target_url) # follow redirects if necessary redirect_url = urllib.request.urlopen(target_url).geturl() if redirect_url and redirect_url != target_url: current_app.logger.debug("followed redirection to %s", redirect_url) target_url = redirect_url parsed_url = urllib.parse.urlparse(target_url) if not parsed_url: current_app.logger.warn( "Could not parse target_url of received webmention: %s", target_url) return None try: # FIXME this is a less-than-perfect fix for hosting from a # subdirectory. The url_map may have some clever work-around. parsed_site_root = urllib.parse.urlparse(get_settings().site_url) site_prefix = parsed_site_root.path if site_prefix.endswith('/'): site_prefix = site_prefix[:-1] if not parsed_url.path.startswith(parsed_site_root.path): raise NotFound urls = current_app.url_map.bind(get_settings().site_url) path = parsed_url.path[len(site_prefix):] current_app.logger.debug('target path with no prefix %s', path) endpoint, args = urls.match(path) current_app.logger.debug('found match for target url %r: %r', endpoint, args) except NotFound: current_app.logger.warn('Webmention could not find target for %s', parsed_url.path) return None post = None if endpoint == 'views.post_by_path': year = args.get('year') month = args.get('month') slug = args.get('slug') post = Post.load_by_path('{}/{:02d}/{}'.format(year, month, slug)) elif endpoint == 'views.post_by_date': post_type = args.get('post_type') year = args.get('year') month = args.get('month') day = args.get('day') index = args.get('index') post = Post.load_by_date(post_type, year, month, day, index) elif endpoint == 'views.post_by_old_date': post_type = args.get('post_type') yymmdd = args.get('yymmdd') year = int('20' + yymmdd[0:2]) month = int(yymmdd[2:4]) day = int(yymmdd[4:6]) post = Post.load_by_date(post_type, year, month, day, index) elif endpoint == 'views.post_by_id': dbid = args.get('dbid') post = Post.load_by_id(dbid) if not post: current_app.logger.warn( "Webmention target points to unknown post: {}".format(args)), return post
def draft_by_hash(hash): post = Post.load_by_path('drafts/{}'.format(hash)) return render_post(post)
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)
def save_edit(): id = request.form.get('post_id') current_app.logger.debug('saving post %s', id) post = Post.load_by_id(id) return save_post(post)
def draft_attachment(hash, filename): post = Post.load_by_path('drafts/{}'.format(hash)) return render_attachment(post, filename)
def do_send_webmentions(post_id, app_config): with async_app_context(app_config): current_app.logger.debug("sending mentions for {}".format(post_id)) post = Post.load_by_id(post_id) if post: return handle_new_or_edit(post)
def save_new(): post_type = request.form.get('post_type', 'note') current_app.logger.debug('saving new post of type %s', post_type) post = Post(post_type) return save_post(post)