def POST_update(self, form, jquery, text):
        if form.has_errors("body", errors.NO_TEXT,
                                   errors.TOO_LONG):
            return

        # create and store the new update
        update = LiveUpdate(data={
            "author_id": c.user._id,
            "body": text,
            "_spam": c.user._spam,
        })

        hooks.get_hook("liveupdate.update").call(update=update)

        LiveUpdateStream.add_update(c.liveupdate_event, update)

        # tell the world about our new update
        builder = LiveUpdateBuilder(None)
        wrapped = builder.wrap_items([update])[0]
        rendered = wrapped.render(style="api")
        _broadcast(type="update", payload=rendered)

        # Queue up parsing any embeds
        queue_parse_embeds(c.liveupdate_event, update)

        # reset the submission form
        t = form.find("textarea")
        t.attr('rows', 3).html("").val("")
예제 #2
0
def parse_embeds(event_id, liveupdate_id, maxwidth=_EMBED_WIDTH):
    """Find, scrape, and store any embeddable URLs in this liveupdate.

    Return the newly altered liveupdate for convenience.
    Note: This should be used in async contexts only.

    """
    if isinstance(liveupdate_id, basestring):
        liveupdate_id = uuid.UUID(liveupdate_id)

    try:
        event = LiveUpdateEvent._byID(
            event_id, read_consistency_level=tdb_cassandra.CL.QUORUM)
        liveupdate = LiveUpdateStream.get_update(
            event,
            liveupdate_id,
            read_consistency_level=tdb_cassandra.CL.QUORUM)
    except tdb_cassandra.NotFound:
        g.log.warning("Couldn't find event/liveupdate for embedding: %r / %r",
                      event_id, liveupdate_id)
        return

    urls = _extract_isolated_urls(liveupdate.body)
    liveupdate.media_objects = _scrape_media_objects(urls, maxwidth=maxwidth)
    liveupdate.mobile_objects = _scrape_mobile_media_objects(urls)
    LiveUpdateStream.add_update(event, liveupdate)

    return liveupdate
def parse_embeds(event_id, liveupdate_id, maxwidth=_EMBED_WIDTH):
    """Find, scrape, and store any embeddable URLs in this liveupdate.

    Return the newly altered liveupdate for convenience.
    Note: This should be used in async contexts only.

    """
    if isinstance(liveupdate_id, basestring):
        liveupdate_id = uuid.UUID(liveupdate_id)

    try:
        event = LiveUpdateEvent._byID(
            event_id, read_consistency_level=tdb_cassandra.CL.QUORUM)
        liveupdate = LiveUpdateStream.get_update(
            event, liveupdate_id, read_consistency_level=tdb_cassandra.CL.QUORUM)
    except tdb_cassandra.NotFound:
        g.log.warning("Couldn't find event/liveupdate for embedding: %r / %r",
                      event_id, liveupdate_id)
        return

    urls = _extract_isolated_urls(liveupdate.body)
    liveupdate.media_objects = _scrape_media_objects(urls, maxwidth=maxwidth)
    liveupdate.mobile_objects = _scrape_mobile_media_objects(urls)
    LiveUpdateStream.add_update(event, liveupdate)

    return liveupdate
    def POST_strike_update(self, form, jquery, update):
        if form.has_errors("id", errors.NO_THING_ID):
            return

        update.stricken = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)

        send_websocket_broadcast(type="strike", payload=update._fullname)
    def POST_strike_update(self, form, jquery, update):
        if form.has_errors("id", errors.NO_THING_ID):
            return

        if not (c.liveupdate_permissions.allow("edit") or
                (c.user_is_loggedin and update.author_id == c.user._id)):
            abort(403)

        update.stricken = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)

        _broadcast(type="strike", payload=update._fullname)
    def GET_focus(self, target):
        try:
            target = uuid.UUID(target)
        except (TypeError, ValueError):
            self.abort404()

        try:
            query = LiveUpdateStream.query_focus(c.liveupdate_event, target)
        except tdb_cassandra.NotFound:
            self.abort404()

        builder = LiveUpdateBuilder(
            query=query, skip=True, reverse=True, num=1, count=0)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        content = pages.LiveUpdateFocusApp(
            event=c.liveupdate_event,
            listing=wrapped_listing,
        )

        return pages.LiveUpdateEventFocusPage(
            content=content,
            page_classes=["liveupdate-focus"],
        ).render()
def close_abandoned_threads():
    """Find live threads that are abandoned and close them.

    Jobs like the activity tracker iterate through all open live threads, so
    closing abandoned threads removes some effort from them and is generally
    good for cleanliness.

    """
    now = datetime.datetime.now(pytz.UTC)
    horizon = now - DERELICTION_THRESHOLD

    for event in LiveUpdateEvent._all():
        if event.state != "live" or event.banned:
            continue

        try:
            columns = LiveUpdateStream._cf.get(
                event._id, column_reversed=True, column_count=1)
        except NotFoundException:
            event_last_modified = event._date
        else:
            updates = LiveUpdateStream._column_to_obj([columns])
            most_recent_update = updates.pop()
            event_last_modified = most_recent_update._date

        if event_last_modified < horizon:
            g.log.warning("Closing %s for inactivity.", event._id)
            close_event(event)
예제 #8
0
def close_abandoned_threads():
    """Find live threads that are abandoned and close them.

    Jobs like the activity tracker iterate through all open live threads, so
    closing abandoned threads removes some effort from them and is generally
    good for cleanliness.

    """
    now = datetime.datetime.now(pytz.UTC)
    horizon = now - DERELICTION_THRESHOLD

    for event in LiveUpdateEvent._all():
        if event.state != "live" or event.banned:
            continue

        try:
            columns = LiveUpdateStream._cf.get(
                event._id, column_reversed=True, column_count=1)
        except NotFoundException:
            event_last_modified = event._date
        else:
            updates = LiveUpdateStream._column_to_obj([columns])
            most_recent_update = updates.pop()
            event_last_modified = most_recent_update._date

        if event_last_modified < horizon:
            g.log.warning("Closing %s for inactivity.", event._id)
            close_event(event)
    def GET_listing(self, num, after, before, count):
        reverse = False
        if before:
            reverse = True
            after = before

        query = LiveUpdateStream.query([c.liveupdate_event._id],
                                       count=num, reverse=reverse)
        if after:
            query.column_start = after

        builder = LiveUpdateBuilder(query=query, skip=True,
                                    reverse=reverse, num=num,
                                    count=count)
        listing = pages.LiveUpdateListing(builder)
        content = pages.LiveUpdateEvent(
            event=c.liveupdate_event,
            listing=listing.listing(),
        )

        # don't generate a url unless this is the main page of an event
        websocket_url = None
        if c.liveupdate_event.state == "live" and not after and not before:
            websocket_url = websockets.make_url(
                "/live/" + c.liveupdate_event._id, max_age=24 * 60 * 60)

        return pages.LiveUpdatePage(
            content=content,
            websocket_url=websocket_url,
        ).render()
    def GET_focus(self, target):
        try:
            target = uuid.UUID(target)
        except (TypeError, ValueError):
            self.abort404()

        try:
            query = LiveUpdateStream.query_focus(c.liveupdate_event, target)
        except tdb_cassandra.NotFound:
            self.abort404()

        builder = LiveUpdateBuilder(
            query=query, skip=True, reverse=True, num=1, count=0)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        content = pages.LiveUpdateFocusApp(
            event=c.liveupdate_event,
            listing=wrapped_listing,
        )

        return pages.LiveUpdateEventFocusPage(
            content=content,
            page_classes=["liveupdate-focus"],
        ).render()
예제 #11
0
    def POST_update(self, form, jquery, text):
        """Post an update to the thread.

        Requires the `update` permission for this thread.

        See also: [/api/live/*thread*/strike_update]
        (#POST_api_live_{thread}_strike_update), and
        [/api/live/*thread*/delete_update]
        (#POST_api_live_{thread}_delete_update).

        """
        if form.has_errors("body", errors.NO_TEXT, errors.TOO_LONG):
            return

        # create and store the new update
        update = LiveUpdate(data={
            "author_id": c.user._id,
            "body": text,
            "_spam": c.user._spam,
        })

        hooks.get_hook("liveupdate.update").call(update=update)

        LiveUpdateStream.add_update(c.liveupdate_event, update)

        # tell the world about our new update
        builder = LiveUpdateBuilder(None)
        wrapped = builder.wrap_items([update])[0]
        rendered = wrapped.render(style="api")
        _broadcast(type="update", payload=rendered)

        amqp.add_item(
            "new_liveupdate_update",
            json.dumps({
                "event_fullname": c.liveupdate_event._fullname,
                "author_fullname": c.user._fullname,
                "liveupdate_id": str(update._id),
                "body": text,
            }))

        liveupdate_events.update_event(update, context=c, request=request)

        # reset the submission form
        t = form.find("textarea")
        t.attr('rows', 3).html("").val("")
예제 #12
0
    def GET_listing(self, num, after, before, count, is_embed):
        reverse = False
        if before:
            reverse = True
            after = before

        query = LiveUpdateStream.query([c.liveupdate_event._id],
                                       count=num, reverse=reverse)
        if after:
            query.column_start = after

        builder = LiveUpdateBuilder(query=query, skip=True,
                                    reverse=reverse, num=num,
                                    count=count)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()
        content = pages.LiveUpdateEventPage(
            event=c.liveupdate_event,
            listing=wrapped_listing,
            show_sidebar=not is_embed,
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + "/about.json",
            Wrapped(c.liveupdate_event),
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        # don't generate a url unless this is the main page of an event
        websocket_url = None
        if c.liveupdate_event.state == "live" and not after and not before:
            websocket_url = websockets.make_url(
                "/live/" + c.liveupdate_event._id, max_age=24 * 60 * 60)

        if not is_embed:
            return pages.LiveUpdatePage(
                content=content,
                websocket_url=websocket_url,
                page_classes=['liveupdate-event'],
            ).render()
        else:
            # ensure we're off the cookie domain before allowing embedding
            if request.host != g.media_domain:
                abort(404)
            c.allow_framing = True

            return pages.LiveUpdateEmbed(
                content=content,
                websocket_url=websocket_url,
                page_classes=['liveupdate-event'],
            ).render()
    def POST_strike_update(self, form, jquery, update):
        """Strike (mark incorrect and cross out) the content of an update.

        Requires that specified update must have been authored by the user or
        that you have the `edit` permission for this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if form.has_errors("id", errors.NO_THING_ID):
            return

        if not (c.liveupdate_permissions.allow("edit") or
                (c.user_is_loggedin and update.author_id == c.user._id)):
            abort(403)

        update.stricken = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)

        _broadcast(type="strike", payload=update._fullname)
    def POST_strike_update(self, form, jquery, update):
        """Strike (mark incorrect and cross out) the content of an update.

        Requires that specified update must have been authored by the user or
        that you have the `edit` permission for this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if form.has_errors("id", errors.NO_THING_ID):
            return

        if not (c.liveupdate_permissions.allow("edit") or
                (c.user_is_loggedin and update.author_id == c.user._id)):
            abort(403)

        update.stricken = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)

        _broadcast(type="strike", payload=update._fullname)
예제 #15
0
    def POST_delete_update(self, form, jquery, update):
        """Delete an update from the thread.

        Requires that specified update must have been authored by the user or
        that you have the `edit` permission for this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if form.has_errors("id", errors.NO_THING_ID):
            return

        if not (c.liveupdate_permissions.allow("edit") or
                (c.user_is_loggedin and update.author_id == c.user._id)):
            abort(403)

        update.deleted = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)
        liveupdate_events.update_event(update, context=c, request=request)

        _broadcast(type="delete", payload=update._fullname)
    def POST_update(self, form, jquery, text):
        if form.has_errors("body", errors.NO_TEXT,
                                   errors.TOO_LONG):
            return

        # create and store the new update
        update = LiveUpdate(data={
            "author_id": c.user._id,
            "body": text,
        })
        LiveUpdateStream.add_update(c.liveupdate_event, update)

        # tell the world about our new update
        builder = LiveUpdateBuilder(None)
        wrapped = builder.wrap_items([update])
        rendered = [w.render() for w in wrapped]
        send_websocket_broadcast(type="update", payload=rendered)

        # reset the submission form
        t = form.find("textarea")
        t.attr('rows', 3).html("").val("")
예제 #17
0
    def POST_delete_update(self, form, jquery, update):
        """Delete an update from the thread.

        Requires that specified update must have been authored by the user or
        that you have the `edit` permission for this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        if form.has_errors("id", errors.NO_THING_ID):
            return

        if not (c.liveupdate_permissions.allow("edit") or
                (c.user_is_loggedin and update.author_id == c.user._id)):
            abort(403)

        update.deleted = True
        LiveUpdateStream.add_update(c.liveupdate_event, update)
        liveupdate_events.update_event(update, context=c, request=request)

        _broadcast(type="delete", payload=update._fullname)
예제 #18
0
    def POST_update(self, form, jquery, text):
        """Post an update to the thread.

        Requires the `update` permission for this thread.

        See also: [/api/live/*thread*/strike_update]
        (#POST_api_live_{thread}_strike_update), and
        [/api/live/*thread*/delete_update]
        (#POST_api_live_{thread}_delete_update).

        """
        if form.has_errors("body", errors.NO_TEXT,
                                   errors.TOO_LONG):
            return

        # create and store the new update
        update = LiveUpdate(data={
            "author_id": c.user._id,
            "body": text,
            "_spam": c.user._spam,
        })

        hooks.get_hook("liveupdate.update").call(update=update)

        LiveUpdateStream.add_update(c.liveupdate_event, update)

        # tell the world about our new update
        builder = LiveUpdateBuilder(None)
        wrapped = builder.wrap_items([update])[0]
        rendered = wrapped.render(style="api")
        _broadcast(type="update", payload=rendered)

        # Queue up parsing any embeds
        queue_parse_embeds(c.liveupdate_event, update)

        liveupdate_events.update_event(update, context=c, request=request)

        # reset the submission form
        t = form.find("textarea")
        t.attr('rows', 3).html("").val("")
    def GET_listing(self, num, after, before, count, is_embed):
        reverse = False
        if before:
            reverse = True
            after = before

        query = LiveUpdateStream.query([c.liveupdate_event._id],
                                       count=num, reverse=reverse)
        if after:
            query.column_start = after

        builder = LiveUpdateBuilder(query=query, skip=True,
                                    reverse=reverse, num=num,
                                    count=count)
        listing = pages.LiveUpdateListing(builder)
        content = pages.LiveUpdateEvent(
            event=c.liveupdate_event,
            listing=listing.listing(),
            show_sidebar=not is_embed,
        )

        # don't generate a url unless this is the main page of an event
        websocket_url = None
        if c.liveupdate_event.state == "live" and not after and not before:
            websocket_url = websockets.make_url(
                "/live/" + c.liveupdate_event._id, max_age=24 * 60 * 60)

        if not is_embed:
            return pages.LiveUpdatePage(
                content=content,
                websocket_url=websocket_url,
            ).render()
        else:
            # embeds are always logged out and therefore safe for frames.
            c.liveupdate_can_manage = False
            c.liveupdate_can_edit = False
            c.allow_framing = True

            return pages.LiveUpdateEmbed(
                content=content,
                websocket_url=websocket_url,
            ).render()
예제 #20
0
    def GET_focus(self, target):
        """Get details about a specific update in a live thread."""
        try:
            target = uuid.UUID(target)
        except (TypeError, ValueError):
            self.abort404()

        try:
            update = LiveUpdateStream.get_update(c.liveupdate_event, target)
        except tdb_cassandra.NotFound:
            self.abort404()

        if update.deleted:
            self.abort404()

        query = FocusQuery([update])

        builder = LiveUpdateBuilder(query=query,
                                    skip=True,
                                    reverse=True,
                                    num=1,
                                    count=0)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        content = pages.LiveUpdateFocusApp(
            event=c.liveupdate_event,
            listing=wrapped_listing,
        )

        return pages.LiveUpdateEventFocusPage(
            content=content,
            focused_update=update,
            page_classes=["liveupdate-focus"],
        ).render()
예제 #21
0
    def GET_listing(self, num, after, before, count, is_embed, style_sr):
        """Get a list of updates posted in this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """

        # preemptively record activity for clients that don't send pixel pings.
        # this won't capture their continued visit, but will at least show a
        # correct activity count for short lived connections.
        record_activity(c.liveupdate_event._id)

        reverse = False
        if before:
            reverse = True
            after = before

        query = LiveUpdateStream.query([c.liveupdate_event._id],
                                       count=num, reverse=reverse)
        if after:
            query.column_start = after
        builder = LiveUpdateBuilder(query=query, skip=True,
                                    reverse=reverse, num=num,
                                    count=count)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()

        if c.user_is_loggedin:
            report_type = LiveUpdateReportsByAccount.get_report(
                c.user, c.liveupdate_event)
        else:
            report_type = None

        content = pages.LiveUpdateEventApp(
            event=c.liveupdate_event,
            listing=wrapped_listing,
            show_sidebar=not is_embed,
            report_type=report_type,
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + "/about.json",
            Wrapped(c.liveupdate_event),
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        if not is_embed:
            return pages.LiveUpdateEventAppPage(
                content=content,
                page_classes=['liveupdate-app'],
            ).render()
        else:
            # ensure we're off the cookie domain before allowing embedding
            if request.host != g.media_domain:
                abort(404)
            c.allow_framing = True

            # interstitial redirects and nsfw settings are funky on the media
            # domain. just disable nsfw embeds.
            if c.liveupdate_event.nsfw:
                embed_page = pages.LiveUpdateEventEmbed(
                    content=pages.LiveUpdateNSFWEmbed(),
                )
                request.environ["usable_error_content"] = embed_page.render()
                abort(403)

            embed_page = pages.LiveUpdateEventEmbed(
                content=content,
                page_classes=['liveupdate-app'],
            )

            if style_sr and getattr(style_sr, "type", "private") != "private":
                c.can_apply_styles = True
                c.allow_styles = True
                embed_page.subreddit_stylesheet_url = \
                    Reddit.get_subreddit_stylesheet_url(style_sr)

            return embed_page.render()
    def GET_listing(self, num, after, before, count, is_embed, style_sr):
        """Get a list of updates posted in this thread.

        See also: [/api/live/*thread*/update](#POST_api_live_{thread}_update).

        """
        reverse = False
        if before:
            reverse = True
            after = before

        query = LiveUpdateStream.query([c.liveupdate_event._id],
                                       count=num, reverse=reverse)
        if after:
            query.column_start = after
        builder = LiveUpdateBuilder(query=query, skip=True,
                                    reverse=reverse, num=num,
                                    count=count)
        listing = pages.LiveUpdateListing(builder)
        wrapped_listing = listing.listing()

        if c.user_is_loggedin:
            report_type = LiveUpdateReportsByAccount.get_report(
                c.user, c.liveupdate_event)
        else:
            report_type = None

        content = pages.LiveUpdateEventApp(
            event=c.liveupdate_event,
            listing=wrapped_listing,
            show_sidebar=not is_embed,
            report_type=report_type,
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + "/about.json",
            Wrapped(c.liveupdate_event),
        )

        c.js_preload.set_wrapped(
            "/live/" + c.liveupdate_event._id + ".json",
            wrapped_listing,
        )

        if not is_embed:
            return pages.LiveUpdateEventAppPage(
                content=content,
                page_classes=['liveupdate-app'],
            ).render()
        else:
            # ensure we're off the cookie domain before allowing embedding
            if request.host != g.media_domain:
                abort(404)
            c.allow_framing = True

            # interstitial redirects and nsfw settings are funky on the media
            # domain. just disable nsfw embeds.
            if c.liveupdate_event.nsfw:
                embed_page = pages.LiveUpdateEventEmbed(
                    content=pages.LiveUpdateNSFWEmbed(),
                )
                request.environ["usable_error_content"] = embed_page.render()
                abort(403)

            embed_page = pages.LiveUpdateEventEmbed(
                content=content,
                page_classes=['liveupdate-app'],
            )

            if style_sr and getattr(style_sr, "type", "private") != "private":
                c.can_apply_styles = True
                c.allow_styles = True
                embed_page.subreddit_stylesheet_url = \
                    Reddit.get_subreddit_stylesheet_url(style_sr)

            return embed_page.render()