Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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})
Ejemplo n.º 6
0
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)