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("")
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)
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()
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("")
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)
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("")
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): """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()
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()
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()