Example #1
0
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)
Example #2
0
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)
Example #3
0
def save_venue(venue):
    venue.name = request.form.get('name')
    venue.location = {
        'latitude': float(request.form.get('latitude')),
        'longitude': float(request.form.get('longitude')),
    }
    venue.update_slug(request.form.get('geocode'))

    if not venue.id:
        db.session.add(venue)
    db.session.commit()

    hooks.fire('venue-saved', venue, request.form)
    return redirect(url_for('venue_by_slug', slug=venue.slug))
Example #4
0
def save_venue(venue):
    venue.name = request.form.get('name')
    venue.location = {
        'latitude': float(request.form.get('latitude')),
        'longitude': float(request.form.get('longitude')),
    }
    venue.update_slug(request.form.get('geocode'))

    if not venue.id:
        db.session.add(venue)
    db.session.commit()

    hooks.fire('venue-saved', venue, request.form)
    return redirect(url_for('.venue_by_slug', slug=venue.slug))
Example #5
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)
Example #6
0
def do_process_webmention(source, target, callback, app_config):
    def call_callback(result):
        if callback:
            requests.post(callback, data=result)

    with async_app_context(app_config):
        try:
            result = interpret_mention(source, target)

            if result.error:
                current_app.logger.warn("Failed to process webmention: %s", result.error)
                response = {
                    "source": source,
                    "target": target,
                    "response_code": 400,
                    "status": "error",
                    "reason": result.error,
                }
                call_callback(response)
                return response

            if result.post and result.delete:
                result.post.mentions = [m for m in result.post.mentions if m.url != source]
            elif result.post:
                result.post.mentions.extend(result.mentions)

            db.session.commit()
            current_app.logger.debug("saved mentions to %s", result.post.path)

            hooks.fire("mention-received", post=result.post)
            if result.post:
                for mres in result.mention_results:
                    if mres.create:
                        send_push_notification(result.post, mres.mention, app_config)

            response = {
                "source": source,
                "target": target,
                "response_code": 200,
                "status": "success",
                "reason": "Deleted"
                if result.delete
                else "Created"
                if any(mres.create for mres in result.mention_results)
                else "Updated",
            }

            call_callback(response)
            return response

        except Exception as e:
            current_app.logger.exception("exception while processing webmention")
            response = {
                "source": source,
                "target": target,
                "response_code": 400,
                "status": "error",
                "reason": "exception while processing webmention {}".format(e),
            }
            call_callback(response)
            return response
Example #7
0
def do_process_webmention(source, target, callback, app_config):
    def call_callback(result):
        if callback:
            requests.post(callback, data=result)
    with async_app_context(app_config):
        try:
            result = interpret_mention(source, target)

            if result.error:
                current_app.logger.warn(
                    'Failed to process webmention: %s', result.error)
                response = {
                    'source': source,
                    'target': target,
                    'response_code': 400,
                    'status': 'error',
                    'reason': result.error
                }
                call_callback(response)
                return response

            if result.post and result.delete:
                result.post.mentions = [m for m in result.post.mentions if
                                        m.url != source]
            elif result.post:
                result.post.mentions.extend(result.mentions)

            elif result.is_person_mention:
                db.session.add_all(result.mentions)

            db.session.commit()
            current_app.logger.debug("saved mentions to %s", result.post.path if result.post else '/')

            hooks.fire('mention-received', post=result.post)
            for mres in result.mention_results:
                if mres.create:
                    send_push_notification(result.post, result.is_person_mention,
                                           mres.mention, app_config)

            response = {
                'source': source,
                'target': target,
                'response_code': 200,
                'status': 'success',
                'reason': 'Deleted' if result.delete
                else 'Created' if any(mres.create for mres
                                      in result.mention_results)
                else 'Updated'
            }

            call_callback(response)
            return response

        except Exception as e:
            current_app.logger.exception(
                "exception while processing webmention")
            response = {
                'source': source,
                'target': target,
                'response_code': 400,
                'status': 'error',
                'reason': "exception while processing webmention {}".format(e)
            }
            call_callback(response)
            return response
Example #8
0
def do_process_webmention(source, target, callback, app_config):
    def call_callback(result):
        if callback:
            requests.post(callback, data=result)

    with async_app_context(app_config):
        try:
            result = interpret_mention(source, target)

            if result.error:
                current_app.logger.warn('Failed to process webmention: %s',
                                        result.error)
                response = {
                    'source': source,
                    'target': target,
                    'response_code': 400,
                    'status': 'error',
                    'reason': result.error
                }
                call_callback(response)
                return response

            if result.post and result.delete:
                result.post.mentions = [
                    m for m in result.post.mentions if m.url != source
                ]
            elif result.post:
                result.post.mentions.extend(result.mentions)

            elif result.is_person_mention:
                db.session.add_all(result.mentions)

            db.session.commit()
            current_app.logger.debug("saved mentions to %s",
                                     result.post.path if result.post else '/')

            hooks.fire('mention-received', post=result.post)
            for mres in result.mention_results:
                if mres.create:
                    send_push_notification(result.post,
                                           result.is_person_mention,
                                           mres.mention, app_config)

            response = {
                'source':
                source,
                'target':
                target,
                'response_code':
                200,
                'status':
                'success',
                'reason':
                'Deleted' if result.delete else 'Created' if any(
                    mres.create
                    for mres in result.mention_results) else 'Updated'
            }

            call_callback(response)
            return response

        except Exception as e:
            current_app.logger.exception(
                "exception while processing webmention")
            response = {
                'source': source,
                'target': target,
                'response_code': 400,
                'status': 'error',
                'reason': "exception while processing webmention {}".format(e)
            }
            call_callback(response)
            return response
Example #9
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)