def GET_wiki_page(self, pv, page_name): """Return the content of a wiki page If `v` is given, show the wiki page as it was at that version If both `v` and `v2` are given, show a diff of the two """ message = None if c.errors.get(('PAGE_NAME_NORMALIZED', 'page')): url = join_urls(c.wiki_base_url, page_name) return self.redirect(url) page, version, version2 = pv if not page: is_api = c.render_style in extensions.API_TYPES if this_may_revise(): if is_api: self.handle_error(404, 'PAGE_NOT_CREATED') errorpage = WikiNotFound(page=page_name) request.environ['usable_error_content'] = errorpage.render() elif is_api: self.handle_error(404, 'PAGE_NOT_FOUND') self.abort404() if version: edit_by = version.get_author() edit_date = version.date else: edit_by = page.get_author() edit_date = page._get('last_edit_date') diffcontent = None if not version: content = page.content if c.is_wiki_mod and page.name in page_descriptions: message = page_descriptions[page.name] else: message = _("viewing revision from %s") % timesince(version.date) if version2: t1 = timesince(version.date) t2 = timesince(version2.date) timestamp1 = _("%s ago") % t1 timestamp2 = _("%s ago") % t2 message = _("comparing revisions from %(date_1)s and %(date_2)s") \ % {'date_1': t1, 'date_2': t2} diffcontent = make_htmldiff(version.content, version2.content, timestamp1, timestamp2) content = version2.content else: message = _("viewing revision from %s ago") % timesince(version.date) content = version.content renderer = RENDERERS_BY_PAGE.get(page.name, 'wiki') return WikiPageView(content, alert=message, v=version, diff=diffcontent, may_revise=this_may_revise(page), edit_by=edit_by, edit_date=edit_date, page=page.name, renderer=renderer).render()
def GET_wiki_page(self, pv, page_name): message = None if c.errors.get(("PAGE_NAME_NORMALIZED", "page")): url = join_urls(c.wiki_base_url, page_name) return self.redirect(url) page, version, version2 = pv if not page: is_api = c.render_style in extensions.API_TYPES if this_may_revise(): if is_api: self.handle_error(404, "PAGE_NOT_CREATED") errorpage = WikiNotFound(page=page_name) request.environ["usable_error_content"] = errorpage.render() elif is_api: self.handle_error(404, "PAGE_NOT_FOUND") self.abort404() if version: edit_by = version.get_author() edit_date = version.date else: edit_by = page.get_author() edit_date = page._get("last_edit_date") diffcontent = None if not version: content = page.content if c.is_wiki_mod and page.name in page_descriptions: message = page_descriptions[page.name] else: message = _("viewing revision from %s") % timesince(version.date) if version2: t1 = timesince(version.date) t2 = timesince(version2.date) timestamp1 = _("%s ago") % t1 timestamp2 = _("%s ago") % t2 message = _("comparing revisions from %(date_1)s and %(date_2)s") % {"date_1": t1, "date_2": t2} diffcontent = make_htmldiff(version.content, version2.content, timestamp1, timestamp2) content = version2.content else: message = _("viewing revision from %s ago") % timesince(version.date) content = version.content renderer = RENDERERS_BY_PAGE.get(page.name, "wiki") return WikiPageView( content, alert=message, v=version, diff=diffcontent, may_revise=this_may_revise(page), edit_by=edit_by, edit_date=edit_date, page=page.name, renderer=renderer, ).render()
def POST_wiki_revision_revert(self, pv, page, revision): if not c.is_wiki_mod: self.handle_error(403, 'MOD_REQUIRED') page, revision = pv content = revision.content author = revision._get('author') reason = 'reverted back %s' % timesince(revision.date) if page.name == 'config/stylesheet': report, parsed = c.site.parse_css(content) if report.errors: self.handle_error(403, 'INVALID_CSS') c.site.change_css(content, parsed, prev=None, reason=reason, force=True) else: try: page.revise(content, author=author, reason=reason, force=True) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", page.revision) c.site._commit() except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', e.max_length) return json.dumps({})
def POST_wiki_revision_revert(self, pv): """Revert a wiki `page` to `revision`""" page, revision = pv if not revision: self.handle_error(400, 'INVALID_REVISION') content = revision.content reason = 'reverted back %s' % timesince(revision.date) if page.name == 'config/stylesheet': css_errors, parsed = c.site.parse_css(content) if css_errors: self.handle_error(403, 'INVALID_CSS') c.site.change_css(content, parsed, prev=None, reason=reason, force=True) else: try: page.revise(content, author=c.user._id36, reason=reason, force=True) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) c.site._commit() except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length) return json.dumps({})
def POST_wiki_revision_revert(self, pv): """Revert a wiki `page` to `revision`""" page, revision = pv if not revision: self.handle_error(400, 'INVALID_REVISION') content = revision.content reason = 'reverted back %s' % timesince(revision.date) if page.name == 'config/stylesheet': css_errors, parsed = c.site.parse_css(content) if css_errors: self.handle_error(403, 'INVALID_CSS') c.site.change_css(content, parsed, prev=None, reason=reason, force=True) else: try: page.revise(content, author=c.user._id36, reason=reason, force=True) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", page.revision) c.site._commit() except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length) return json.dumps({})
def POST_wiki_revision_revert(self, pv): """Revert a wiki `page` to `revision`""" page, revision = pv if not revision: self.handle_error(400, "INVALID_REVISION") VNotInTimeout().run(action_name="wikirevise", details_text="revision_revert", target=page) content = revision.content reason = "reverted back %s" % timesince(revision.date) if page.name == "config/stylesheet": css_errors, parsed = c.site.parse_css(content) if css_errors: self.handle_error(403, "INVALID_CSS") c.site.change_css(content, parsed, prev=None, reason=reason, force=True) else: try: page.revise(content, author=c.user._id36, reason=reason, force=True) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.name in ATTRIBUTE_BY_PAGE: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) c.site._commit() except ContentLengthError as e: self.handle_error(403, "CONTENT_LENGTH_ERROR", max_length=e.max_length) return json.dumps({})
def simplified_timesince(date, include_tense=True): if date > timeago("1 minute"): return _("just now") since = [] since.append(timesince(date)) if include_tense: since.append(_("ago")) return " ".join(since)
def simplified_timesince(date, include_tense=True): if date > timeago("1 minute"): return _("just now") since = timesince(date) if include_tense: return _("%s ago") % since else: return since
def GET_wiki_page(self, pv, page_name): message = None if c.errors.get(('PAGE_NAME_NORMALIZED', 'page')): url = join_urls(c.wiki_base_url, page_name) return self.redirect(url) page, version, version2 = pv if not page: if c.render_style in extensions.API_TYPES: self.handle_error(404, 'PAGE_NOT_CREATED') return WikiNotFound(page=page_name).render() if version: edit_by = version.get_author() edit_date = version.date else: edit_by = page.get_author() edit_date = page._get('last_edit_date') diffcontent = None if not version: content = page.content if c.is_wiki_mod and page.name in page_descriptions: message = page_descriptions[page.name] else: message = _("viewing revision from %s") % timesince(version.date) if version2: t1 = timesince(version.date) t2 = timesince(version2.date) timestamp1 = _("%s ago") % t1 timestamp2 = _("%s ago") % t2 message = _("comparing revisions from %(date_1)s and %(date_2)s") \ % {'date_1': t1, 'date_2': t2} diffcontent = make_htmldiff(version.content, version2.content, timestamp1, timestamp2) content = version2.content else: message = _("viewing revision from %s ago") % timesince(version.date) content = version.content return WikiPageView(content, alert=message, v=version, diff=diffcontent, may_revise=this_may_revise(page), edit_by=edit_by, edit_date=edit_date, page=page.name).render()
def GET_wiki_page(self, pv): page, version, version2 = pv message = None if not page: return self.GET_wiki_create(page=c.page, view=True) if version: edit_by = version.author_name() edit_date = version.date else: edit_by = page.author_name() edit_date = page._get('last_edit_date') diffcontent = None if not version: content = page.content if c.is_wiki_mod and page.name in page_descriptions: message = page_descriptions[page.name] else: message = _("viewing revision from %s") % timesince(version.date) if version2: t1 = timesince(version.date) t2 = timesince(version2.date) timestamp1 = _("%s ago") % t1 timestamp2 = _("%s ago") % t2 message = _("comparing revisions from %(date_1)s and %(date_2)s") \ % {'date_1': t1, 'date_2': t2} diffcontent = make_htmldiff(version.content, version2.content, timestamp1, timestamp2) content = version2.content else: message = _("viewing revision from %s ago") % timesince( version.date) content = version.content return WikiPageView(content, alert=message, v=version, diff=diffcontent, edit_by=edit_by, edit_date=edit_date).render()
def __init__(self, system, subject): from r2.models.admin_notes import AdminNotesBySystem self.system = system self.subject = subject self.author = c.user.name self.notes = AdminNotesBySystem.in_display_order(system, subject) # Convert timestamps for easier reading/translation for note in self.notes: note["timesince"] = timesince(note["when"]) Templated.__init__(self)
def __init__(self, system, subject, subject_user=None): from r2.models.admin_notes import AdminNotesBySystem self.system = system self.subject = subject self.author = c.user.name self.notes = AdminNotesBySystem.in_display_order(system, subject) self.global_ban = 'yes' if subject_user and subject_user.is_global_banned else 'no' # Convert timestamps for easier reading/translation for note in self.notes: note["timesince"] = timesince(note["when"]) Templated.__init__(self)
def GET_wiki_page(self, pv): page, version, version2 = pv message = None if not page: return self.GET_wiki_create(page=c.page, view=True) if version: edit_by = version.author_name() edit_date = version.date else: edit_by = page.author_name() edit_date = page._get('last_edit_date') diffcontent = None if not version: content = page.content if c.is_wiki_mod and page.name in page_descriptions: message = page_descriptions[page.name] else: message = _("viewing revision from %s") % timesince(version.date) if version2: t1 = timesince(version.date) t2 = timesince(version2.date) timestamp1 = _("%s ago") % t1 timestamp2 = _("%s ago") % t2 message = _("comparing revisions from %(date_1)s and %(date_2)s") \ % {'date_1': t1, 'date_2': t2} diffcontent = make_htmldiff(version.content, version2.content, timestamp1, timestamp2) content = version2.content else: message = _("viewing revision from %s ago") % timesince(version.date) content = version.content return WikiPageView(content, alert=message, v=version, diff=diffcontent, edit_by=edit_by, edit_date=edit_date).render()
def POST_wiki_revision_revert(self, pv, page, revision): if not c.is_wiki_mod: self.handle_error(403, 'MOD_REQUIRED') page, revision = pv content = revision.content author = revision._get('author') reason = 'reverted back %s' % timesince(revision.date) if page.name == 'config/stylesheet': report, parsed = c.site.parse_css(content) if report.errors: self.handle_error(403, 'INVALID_CSS') c.site.change_css(content, parsed, prev=None, reason=reason, force=True) else: try: page.revise(content, author=author, reason=reason, force=True) except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', e.max_length) return json.dumps({})
def _replace_render(style=None, display=True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or 'html' replacements = {} if hasattr(item, 'child'): if item.child: replacements['childlisting'] = item.child.render(style=style) else: # Special case for when the comment tree wasn't built which # occurs both in the inbox and spam page view of comments. replacements['childlisting'] = None else: replacements['childlisting'] = '' #only LinkListing has a show_nums attribute if listing: if hasattr(listing, "show_nums"): if listing.show_nums: num_str = str(item.num) if hasattr(listing, "num_margin"): num_margin = str(listing.num_margin) else: num_margin = "%.2fex" % (len(str(listing.max_num)) * 1.1) else: num_str = '' num_margin = "0px;display:none" replacements["numcolmargin"] = num_margin replacements["num"] = num_str if hasattr(listing, "max_score"): mid_margin = len(str(listing.max_score)) if hasattr(listing, "mid_margin"): mid_margin = str(listing.mid_margin) elif mid_margin == 1: mid_margin = "15px" else: mid_margin = "%dex" % (mid_margin + 1) replacements["midcolmargin"] = mid_margin #$votehash is only present when voting arrows are present if c.user_is_loggedin: replacements['votehash'] = vote_hash(c.user, item, listing.vote_hash_type) if hasattr(item, "num_comments"): com_label, com_cls = comment_label(item.num_comments) if style == "compact": com_label = unicode(item.num_comments) replacements['numcomments'] = com_label replacements['commentcls'] = com_cls replacements['display'] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub (replacements['scoredislikes'], replacements['scoreunvoted'], replacements['scorelikes']) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): if hasattr(item, "promoted") and item.promoted is not None: from r2.lib import promote # promoted links are special in their date handling replacements['timesince'] = timesince(item._date - promote.timezone_offset) else: replacements['timesince'] = timesince(item._date) replacements['time_period'] = calc_time_period(item._date) # compute the last edited time here so we don't end up caching it if hasattr(item, "editted") and not isinstance(item.editted, bool): replacements['lastedited'] = timesince(item.editted) # Set in front.py:GET_comments() replacements['previous_visits_hex'] = c.previous_visits_hex renderer = render_func or item.render res = renderer(style=style, **replacements) if isinstance(res, (str, unicode)): rv = unsafe(res) if g.debug: for leftover in re.findall('<\$>(.+?)(?:<|$)', rv): print "replace_render didn't replace %s" % leftover return rv return res
def _replace_render(style = None, display = True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or 'html' replacements = {} child_txt = ( hasattr(item, "child") and item.child )\ and item.child.render(style = style) or "" replacements["childlisting"] = child_txt #only LinkListing has a show_nums attribute if listing: if hasattr(listing, "show_nums"): if listing.show_nums: num_str = str(item.num) if hasattr(listing, "num_margin"): num_margin = str(listing.num_margin) else: num_margin = "%.2fex" % (len(str(listing.max_num))*1.1) else: num_str = '' num_margin = "0px;display:none" replacements["numcolmargin"] = num_margin replacements["num"] = num_str if hasattr(listing, "max_score"): mid_margin = len(str(listing.max_score)) if hasattr(listing, "mid_margin"): mid_margin = str(listing.mid_margin) elif mid_margin == 1: mid_margin = "15px" else: mid_margin = "%dex" % (mid_margin+1) replacements["midcolmargin"] = mid_margin #$votehash is only present when voting arrows are present if c.user_is_loggedin: replacements['votehash'] = vote_hash(c.user, item, listing.vote_hash_type) if hasattr(item, "num_comments"): if not item.num_comments: # generates "comment" the imperative verb com_label = _("comment {verb}") com_cls = 'comments empty' else: # generates "XX comments" as a noun com_label = ungettext("comment", "comments", item.num_comments) com_label = strings.number_label % dict(num=item.num_comments, thing=com_label) com_cls = 'comments' if style == "compact": com_label = unicode(item.num_comments) replacements['numcomments'] = com_label replacements['commentcls'] = com_cls replacements['display'] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub (replacements['scoredislikes'], replacements['scoreunvoted'], replacements['scorelikes']) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): if hasattr(item, "promoted") and item.promoted is not None: from r2.lib import promote # promoted links are special in their date handling replacements['timesince'] = timesince(item._date - promote.timezone_offset) else: replacements['timesince'] = timesince(item._date) replacements['time_period'] = calc_time_period(item._date) # Set in front.py:GET_comments() replacements['previous_visits_hex'] = c.previous_visits_hex renderer = render_func or item.render res = renderer(style = style, **replacements) if isinstance(res, (str, unicode)): rv = unsafe(res) if g.debug: for leftover in re.findall('<\$>(.+?)(?:<|$)', rv): print "replace_render didn't replace %s" % leftover return rv return res
def wrap_items(self, items): from r2.lib.db import queries from r2.lib.template_helpers import ( add_friend_distinguish, add_admin_distinguish, add_moderator_distinguish, add_cakeday_distinguish, add_special_distinguish, ) user = c.user if c.user_is_loggedin else None aids = set(l.author_id for l in items if hasattr(l, "author_id") and l.author_id is not None) authors = Account._byID(aids, data=True, stale=self.stale) now = datetime.datetime.now(g.tz) cakes = {a._id for a in authors.itervalues() if a.cake_expiration and a.cake_expiration >= now} friend_rels = user.friend_rels() if user and user.gold else {} subreddits = Subreddit.load_subreddits(items, stale=self.stale) can_ban_set = set() if user: for sr_id, sr in subreddits.iteritems(): if sr.can_ban(user): can_ban_set.add(sr_id) # get likes/dislikes try: likes = queries.get_likes(user, items) except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: g.log.warning("Cassandra vote lookup failed: %r", e) likes = {} types = {} wrapped = [] for item in items: w = self.wrap(item) wrapped.append(w) # add for caching (plus it should be bad form to use _ # variables in templates) w.fullname = item._fullname types.setdefault(w.render_class, []).append(w) w.author = None w.friend = False w.distinguished = None if hasattr(item, "distinguished"): if item.distinguished == "yes": w.distinguished = "moderator" elif item.distinguished in ("admin", "special", "gold", "gold-auto"): w.distinguished = item.distinguished if getattr(item, "author_id", None): w.author = authors.get(item.author_id) if hasattr(item, "sr_id") and item.sr_id is not None: w.subreddit = subreddits[item.sr_id] distinguish_attribs_list = [] # if display_author exists, then w.author is unknown to the # receiver, so we can't check for friend or cakeday author_is_hidden = hasattr(item, "display_author") if not author_is_hidden and user and w.author and w.author._id in user.friends: w.friend = True # TODO: deprecated? if item.author_id in friend_rels: note = getattr(friend_rels[w.author._id], "note", None) else: note = None add_friend_distinguish(distinguish_attribs_list, note) if w.distinguished == "admin" and w.author: add_admin_distinguish(distinguish_attribs_list) if w.distinguished == "moderator": add_moderator_distinguish(distinguish_attribs_list, w.subreddit) if w.distinguished == "special": add_special_distinguish(distinguish_attribs_list, w.author) if not author_is_hidden and w.author and w.author._id in cakes and not c.profilepage: add_cakeday_distinguish(distinguish_attribs_list, w.author) w.attribs = distinguish_attribs_list user_vote_dir = likes.get((user, item)) if user_vote_dir == Vote.DIRECTIONS.up: w.likes = True elif user_vote_dir == Vote.DIRECTIONS.down: w.likes = False else: w.likes = None w.upvotes = item._ups w.downvotes = item._downs total_votes = max(item.num_votes, 1) w.upvote_ratio = float(item._ups) / total_votes w.is_controversial = self._is_controversial(w) w.score = w.upvotes - w.downvotes if user_vote_dir == Vote.DIRECTIONS.up: base_score = w.score - 1 elif user_vote_dir == Vote.DIRECTIONS.down: base_score = w.score + 1 else: base_score = w.score # store the set of available scores based on the vote # for ease of i18n when there is a label w.voting_score = [(base_score + x - 1) for x in range(3)] w.deleted = item._deleted w.link_notes = [] if c.user_is_admin: if item._deleted: w.link_notes.append("deleted link") if getattr(item, "verdict", None): if not item.verdict.endswith("-approved"): w.link_notes.append(w.verdict) if c.user_is_admin and getattr(item, "ip", None): w.ip_span = ip_span(item.ip) else: w.ip_span = "" # if the user can ban things on a given subreddit, or an # admin, then allow them to see that the item is spam, and # add the other spam-related display attributes w.show_reports = False w.show_spam = False w.can_ban = False w.use_big_modbuttons = self.spam_listing if c.user_is_admin or (user and hasattr(item, "sr_id") and item.sr_id in can_ban_set): if getattr(item, "promoted", None) is None: w.can_ban = True ban_info = getattr(item, "ban_info", {}) w.unbanner = ban_info.get("unbanner") if item._spam: w.show_spam = True w.moderator_banned = ban_info.get("moderator_banned", False) w.autobanned = ban_info.get("auto", False) w.banner = ban_info.get("banner") w.banned_at = ban_info.get("banned_at", None) if ban_info.get("note", None) and w.banner: w.banner += " (%s)" % ban_info["note"] w.use_big_modbuttons = True if getattr(w, "author", None) and w.author._spam: w.show_spam = "author" if c.user == w.author and c.user._spam: w.show_spam = False w._spam = False w.use_big_modbuttons = False elif getattr(item, "reported", 0) > 0 and ( not getattr(item, "ignore_reports", False) or c.user_is_admin ): w.show_reports = True w.use_big_modbuttons = True # report_count isn't used in any template, but add it to # the Wrapped so it's pulled into the render cache key in # instances when reported will be used in the template w.report_count = item.reported w.approval_checkmark = None if w.can_ban: verdict = getattr(w, "verdict", None) if verdict in ("admin-approved", "mod-approved"): approver = None approval_time = None baninfo = getattr(w, "ban_info", None) if baninfo: approver = baninfo.get("unbanner", None) approval_time = baninfo.get("unbanned_at", None) approver = approver or _("a moderator") if approval_time: text = _("approved by %(who)s %(when)s ago") % { "who": approver, "when": timesince(approval_time), } else: text = _("approved by %s") % approver w.approval_checkmark = text hooks.get_hook("builder.wrap_items").call(item=item, wrapped=w) # recache the user object: it may be None if user is not logged in, # whereas now we are happy to have the UnloggedUser object user = c.user for cls in types.keys(): cls.add_props(user, types[cls]) return wrapped
def _replace_render(style = None, display = True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or 'html' replacements = {} child_txt = ( hasattr(item, "child") and item.child )\ and item.child.render(style = style) or "" replacements["childlisting"] = child_txt #only LinkListing has a show_nums attribute if listing: if hasattr(listing, "show_nums"): if listing.show_nums: num_str = str(item.num) if hasattr(listing, "num_margin"): num_margin = str(listing.num_margin) else: num_margin = "%.2fex" % (len(str(listing.max_num))*1.1) else: num_str = '' num_margin = "0px" replacements["numcolmargin"] = num_margin replacements["num"] = num_str if hasattr(listing, "max_score"): mid_margin = len(str(listing.max_score)) if hasattr(listing, "mid_margin"): mid_margin = str(listing.mid_margin) elif mid_margin == 1: mid_margin = "15px" else: mid_margin = "%dex" % (mid_margin+1) replacements["midcolmargin"] = mid_margin #$votehash is only present when voting arrows are present if c.user_is_loggedin: replacements['votehash'] = vote_hash(c.user, item, listing.vote_hash_type) if hasattr(item, "num_comments"): if not item.num_comments: # generates "comment" the imperative verb com_label = _("comment {verb}") com_cls = 'comments empty' else: # generates "XX comments" as a noun com_label = ungettext("comment", "comments", item.num_comments) com_label = strings.number_label % dict(num=item.num_comments, thing=com_label) com_cls = 'comments' replacements['numcomments'] = com_label replacements['commentcls'] = com_cls replacements['display'] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub (replacements['scoredislikes'], replacements['scoreunvoted'], replacements['scorelikes']) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): replacements['timesince'] = timesince(item._date) renderer = render_func or item.render res = renderer(style = style, **replacements) if isinstance(res, (str, unicode)): return unsafe(res) return res
def wrap_items(self, items): from r2.lib.db import queries from r2.lib.template_helpers import add_attr user = c.user if c.user_is_loggedin else None aids = set(l.author_id for l in items if hasattr(l, 'author_id') and l.author_id is not None) authors = Account._byID(aids, data=True, stale=self.stale) now = datetime.datetime.now(g.tz) cakes = {a._id for a in authors.itervalues() if a.cake_expiration and a.cake_expiration >= now} friend_rels = user.friend_rels() if user and user.gold else {} subreddits = Subreddit.load_subreddits(items, stale=self.stale) can_ban_set = set() can_flair_set = set() can_own_flair_set = set() if user: for sr_id, sr in subreddits.iteritems(): if sr.can_ban(user): can_ban_set.add(sr_id) if sr.is_moderator_with_perms(user, 'flair'): can_flair_set.add(sr_id) if sr.link_flair_self_assign_enabled: can_own_flair_set.add(sr_id) #get likes/dislikes try: likes = queries.get_likes(user, items) except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: g.log.warning("Cassandra vote lookup failed: %r", e) likes = {} types = {} wrapped = [] modlink = {} modlabel = {} for s in subreddits.values(): modlink[s._id] = '/r/%s/about/moderators' % s.name modlabel[s._id] = (_('moderator of /r/%(reddit)s, ' 'speaking officially') % {'reddit': s.name}) for item in items: w = self.wrap(item) wrapped.append(w) # add for caching (plus it should be bad form to use _ # variables in templates) w.fullname = item._fullname types.setdefault(w.render_class, []).append(w) w.author = None w.friend = False # List of tuples (see add_attr() for details) w.attribs = [] w.distinguished = None if hasattr(item, "distinguished"): if item.distinguished == 'yes': w.distinguished = 'moderator' elif item.distinguished in ('admin', 'special', 'gold', 'gold-auto'): w.distinguished = item.distinguished try: w.author = authors.get(item.author_id) if user and item.author_id in user.friends: # deprecated old way: w.friend = True # new way: label = None if friend_rels: rel = friend_rels[item.author_id] note = getattr(rel, "note", None) if note: label = u"%s (%s)" % (_("friend"), _force_unicode(note)) add_attr(w.attribs, 'F', label) except AttributeError: pass if (w.distinguished == 'admin' and w.author): add_attr(w.attribs, 'A') if w.distinguished == 'moderator': add_attr(w.attribs, 'M', label=modlabel[item.sr_id], link=modlink[item.sr_id]) if w.distinguished == 'special': args = w.author.special_distinguish() args.pop('name') if not args.get('kind'): args['kind'] = 'special' add_attr(w.attribs, **args) if w.author and w.author._id in cakes and not c.profilepage: add_attr( w.attribs, kind="cake", label=(_("%(user)s just celebrated a reddit birthday!") % {"user": w.author.name}), link="/user/%s" % w.author.name, ) if hasattr(item, "sr_id") and item.sr_id is not None: w.subreddit = subreddits[item.sr_id] w.likes = likes.get((user, item)) # update vote tallies compute_votes(w, item) w.score = w.upvotes - w.downvotes if w.likes: base_score = w.score - 1 elif w.likes is None: base_score = w.score else: base_score = w.score + 1 # store the set of available scores based on the vote # for ease of i18n when there is a label w.voting_score = [(base_score + x - 1) for x in range(3)] w.deleted = item._deleted w.link_notes = [] if c.user_is_admin: if item._deleted: w.link_notes.append("deleted link") if getattr(item, "verdict", None): if not item.verdict.endswith("-approved"): w.link_notes.append(w.verdict) if c.user_is_admin and getattr(item, 'ip', None): w.ip_span = ip_span(item.ip) else: w.ip_span = "" # if the user can ban things on a given subreddit, or an # admin, then allow them to see that the item is spam, and # add the other spam-related display attributes w.show_reports = False w.show_spam = False w.can_ban = False w.can_flair = False w.use_big_modbuttons = self.spam_listing if (c.user_is_admin or (user and hasattr(item,'sr_id') and item.sr_id in can_ban_set)): if getattr(item, "promoted", None) is None: w.can_ban = True ban_info = getattr(item, 'ban_info', {}) w.unbanner = ban_info.get('unbanner') if item._spam: w.show_spam = True w.moderator_banned = ban_info.get('moderator_banned', False) w.autobanned = ban_info.get('auto', False) w.banner = ban_info.get('banner') if ban_info.get('note', None) and w.banner: w.banner += ' (%s)' % ban_info['note'] w.use_big_modbuttons = True if getattr(w, "author", None) and w.author._spam: w.show_spam = "author" if c.user == w.author and c.user._spam: w.show_spam = False w._spam = False w.use_big_modbuttons = False elif (getattr(item, 'reported', 0) > 0 and (not getattr(item, 'ignore_reports', False) or c.user_is_admin)): w.show_reports = True w.use_big_modbuttons = True if (c.user_is_admin or (user and hasattr(item, 'sr_id') and (item.sr_id in can_flair_set or (w.author and w.author._id == user._id and item.sr_id in can_own_flair_set)))): w.can_flair = True w.approval_checkmark = None if w.can_ban: verdict = getattr(w, "verdict", None) if verdict in ('admin-approved', 'mod-approved'): approver = None approval_time = None baninfo = getattr(w, "ban_info", None) if baninfo: approver = baninfo.get("unbanner", None) approval_time = baninfo.get("unbanned_at", None) approver = approver or _("a moderator") if approval_time: text = _("approved by %(who)s %(when)s ago") % { "who": approver, "when": timesince(approval_time)} else: text = _("approved by %s") % approver w.approval_checkmark = text # recache the user object: it may be None if user is not logged in, # whereas now we are happy to have the UnloggedUser object user = c.user for cls in types.keys(): cls.add_props(user, types[cls]) return wrapped
def _replace_render(style = None, display = True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or 'html' replacements = {} if hasattr(item, 'child'): if item.child: replacements['childlisting'] = item.child.render(style=style) else: # Special case for when the comment tree wasn't built which # occurs both in the inbox and spam page view of comments. replacements['childlisting'] = None else: replacements['childlisting'] = '' #only LinkListing has a show_nums attribute if listing: if hasattr(listing, "show_nums"): if listing.show_nums: num_str = str(item.num) if hasattr(listing, "num_margin"): num_margin = str(listing.num_margin) else: num_margin = "%.2fex" % (len(str(listing.max_num))*1.1) else: num_str = '' num_margin = "0px;display:none" replacements["numcolmargin"] = num_margin replacements["num"] = num_str if hasattr(listing, "max_score"): mid_margin = len(str(listing.max_score)) if hasattr(listing, "mid_margin"): mid_margin = str(listing.mid_margin) elif mid_margin == 1: mid_margin = "15px" else: mid_margin = "%dex" % (mid_margin+1) replacements["midcolmargin"] = mid_margin #$votehash is only present when voting arrows are present if c.user_is_loggedin: replacements['votehash'] = vote_hash(c.user, item, listing.vote_hash_type) if hasattr(item, "num_comments"): com_label, com_cls = comment_label(item.num_comments) if style == "compact": com_label = unicode(item.num_comments) replacements['numcomments'] = com_label replacements['commentcls'] = com_cls replacements['display'] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub (replacements['scoredislikes'], replacements['scoreunvoted'], replacements['scorelikes']) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): if hasattr(item, "promoted") and item.promoted is not None: from r2.lib import promote # promoted links are special in their date handling replacements['timesince'] = timesince(item._date - promote.timezone_offset) else: replacements['timesince'] = timesince(item._date) replacements['time_period'] = calc_time_period(item._date) # compute the last edited time here so we don't end up caching it if hasattr(item, "editted") and not isinstance(item.editted, bool): replacements['lastedited'] = timesince(item.editted) # Set in front.py:GET_comments() replacements['previous_visits_hex'] = c.previous_visits_hex renderer = render_func or item.render res = renderer(style = style, **replacements) if isinstance(res, (str, unicode)): rv = unsafe(res) if g.debug: for leftover in re.findall('<\$>(.+?)(?:<|$)', rv): print "replace_render didn't replace %s" % leftover return rv return res
def wrap_items(self, items): user = c.user if c.user_is_loggedin else None #get authors #TODO pull the author stuff into add_props for links and #comments and messages? try: aids = set(l.author_id for l in items) except AttributeError: aids = None authors = Account._byID(aids, True) if aids else {} # srids = set(l.sr_id for l in items if hasattr(l, "sr_id")) subreddits = Subreddit.load_subreddits(items) if not user: can_ban_set = set() else: can_ban_set = set(id for (id, sr) in subreddits.iteritems() if sr.can_ban(user)) #get likes/dislikes #TODO Vote.likes should accept empty lists likes = Vote.likes(user, items) if user and items else {} reports = Report.fastreported(user, items) if user else {} uid = user._id if user else None # we'll be grabbing this in the spam processing below if c.user_is_admin: ban_info = admintools.ban_info([x for x in items if x._spam]) elif user and len(can_ban_set) > 0: ban_info = admintools.ban_info([ x for x in items if (x._spam and hasattr(x, 'sr_id') and x.sr_id in can_ban_set) ]) else: ban_info = dict() types = {} wrapped = [] count = 0 for item in items: w = self.wrap(item) wrapped.append(w) types.setdefault(w.render_class, []).append(w) #TODO pull the author stuff into add_props for links and #comments and messages? try: w.author = authors.get(item.author_id) w.friend = item.author_id in user.friends if user else False except AttributeError: w.author = None w.friend = False if hasattr(item, "sr_id"): w.subreddit = subreddits[item.sr_id] vote = likes.get((user, item)) if vote: w.likes = (True if vote._name == '1' else False if vote._name == '-1' else None) else: w.likes = None #definite w.timesince = utils.timesince(item._date) # update vote tallies compute_votes(w, item) w.score = [w.upvotes, w.downvotes] w.deleted = item._deleted w.rowstyle = w.rowstyle if hasattr(w, 'rowstyle') else '' w.rowstyle += ' ' + ('even' if (count % 2) else 'odd') count += 1 # would have called it "reported", but that is already # taken on the thing itself as "how many total # reports". Indicates whether this user reported this # item, and should be visible to all users w.report_made = reports.get((user, item, Report._field)) # if the user can ban things on a given subreddit, or an # admin, then allow them to see that the item is spam, and # add the other spam-related display attributes w.show_reports = False w.show_spam = False w.can_ban = False if (c.user_is_admin or (user and hasattr(item, 'sr_id') and item.sr_id in can_ban_set)): w.can_ban = True if item._spam: w.show_spam = True if not hasattr(item, 'moderator_banned'): w.moderator_banned = False w.autobanned, w.banner = ban_info.get( item._fullname, (False, None)) elif hasattr(item, 'reported') and item.reported > 0: w.show_reports = True for cls in types.keys(): cls.add_props(user, types[cls]) return wrapped
def wrap_items(self, items): from r2.lib.db import queries from r2.lib.template_helpers import add_attr user = c.user if c.user_is_loggedin else None aids = set(l.author_id for l in items if hasattr(l, 'author_id') and l.author_id is not None) authors = Account._byID(aids, data=True, stale=self.stale) now = datetime.datetime.now(g.tz) cakes = { a._id for a in authors.itervalues() if a.cake_expiration and a.cake_expiration >= now } friend_rels = user.friend_rels() if user and user.gold else {} subreddits = Subreddit.load_subreddits(items, stale=self.stale) can_ban_set = set() if user: for sr_id, sr in subreddits.iteritems(): if sr.can_ban(user): can_ban_set.add(sr_id) #get likes/dislikes try: likes = queries.get_likes(user, items) except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: g.log.warning("Cassandra vote lookup failed: %r", e) likes = {} types = {} wrapped = [] modlink = {} modlabel = {} for s in subreddits.values(): modlink[s._id] = '/r/%s/about/moderators' % s.name modlabel[s._id] = (_('moderator of /r/%(reddit)s, ' 'speaking officially') % { 'reddit': s.name }) for item in items: w = self.wrap(item) wrapped.append(w) # add for caching (plus it should be bad form to use _ # variables in templates) w.fullname = item._fullname types.setdefault(w.render_class, []).append(w) w.author = None w.friend = False # List of tuples (see add_attr() for details) w.attribs = [] w.distinguished = None if hasattr(item, "distinguished"): if item.distinguished == 'yes': w.distinguished = 'moderator' elif item.distinguished in ('admin', 'special', 'gold', 'gold-auto'): w.distinguished = item.distinguished try: w.author = authors.get(item.author_id) if user and item.author_id in user.friends: # deprecated old way: w.friend = True # new way: label = None if friend_rels: rel = friend_rels[item.author_id] note = getattr(rel, "note", None) if note: label = u"%s (%s)" % (_("friend"), _force_unicode(note)) add_attr(w.attribs, 'F', label) except AttributeError: pass if (w.distinguished == 'admin' and w.author): add_attr(w.attribs, 'A') if w.distinguished == 'moderator': add_attr(w.attribs, 'M', label=modlabel[item.sr_id], link=modlink[item.sr_id]) if w.distinguished == 'special': args = w.author.special_distinguish() args.pop('name') if not args.get('kind'): args['kind'] = 'special' add_attr(w.attribs, **args) if w.author and w.author._id in cakes and not c.profilepage: add_attr( w.attribs, kind="cake", label=(_("%(user)s just celebrated a reddit birthday!") % { "user": w.author.name }), link="/user/%s" % w.author.name, ) if hasattr(item, "sr_id") and item.sr_id is not None: w.subreddit = subreddits[item.sr_id] w.likes = likes.get((user, item)) # update vote tallies compute_votes(w, item) w.score = w.upvotes - w.downvotes if w.likes: base_score = w.score - 1 elif w.likes is None: base_score = w.score else: base_score = w.score + 1 # store the set of available scores based on the vote # for ease of i18n when there is a label w.voting_score = [(base_score + x - 1) for x in range(3)] w.deleted = item._deleted w.link_notes = [] if c.user_is_admin: if item._deleted: w.link_notes.append("deleted link") if getattr(item, "verdict", None): if not item.verdict.endswith("-approved"): w.link_notes.append(w.verdict) if c.user_is_admin and getattr(item, 'ip', None): w.ip_span = ip_span(item.ip) else: w.ip_span = "" # if the user can ban things on a given subreddit, or an # admin, then allow them to see that the item is spam, and # add the other spam-related display attributes w.show_reports = False w.show_spam = False w.can_ban = False w.use_big_modbuttons = self.spam_listing if (c.user_is_admin or (user and hasattr(item, 'sr_id') and item.sr_id in can_ban_set)): if getattr(item, "promoted", None) is None: w.can_ban = True ban_info = getattr(item, 'ban_info', {}) w.unbanner = ban_info.get('unbanner') if item._spam: w.show_spam = True w.moderator_banned = ban_info.get('moderator_banned', False) w.autobanned = ban_info.get('auto', False) w.banner = ban_info.get('banner') w.banned_at = ban_info.get("banned_at", None) if ban_info.get('note', None) and w.banner: w.banner += ' (%s)' % ban_info['note'] w.use_big_modbuttons = True if getattr(w, "author", None) and w.author._spam: w.show_spam = "author" if c.user == w.author and c.user._spam: w.show_spam = False w._spam = False w.use_big_modbuttons = False elif (getattr(item, 'reported', 0) > 0 and (not getattr(item, 'ignore_reports', False) or c.user_is_admin)): w.show_reports = True w.use_big_modbuttons = True # report_count isn't used in any template, but add it to # the Wrapped so it's pulled into the render cache key in # instances when reported will be used in the template w.report_count = item.reported w.approval_checkmark = None if w.can_ban: verdict = getattr(w, "verdict", None) if verdict in ('admin-approved', 'mod-approved'): approver = None approval_time = None baninfo = getattr(w, "ban_info", None) if baninfo: approver = baninfo.get("unbanner", None) approval_time = baninfo.get("unbanned_at", None) approver = approver or _("a moderator") if approval_time: text = _("approved by %(who)s %(when)s ago") % { "who": approver, "when": timesince(approval_time) } else: text = _("approved by %s") % approver w.approval_checkmark = text # recache the user object: it may be None if user is not logged in, # whereas now we are happy to have the UnloggedUser object user = c.user for cls in types.keys(): cls.add_props(user, types[cls]) return wrapped
def _replace_render(style=None, display=True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or 'html' replacements = {} child_txt = ( hasattr(item, "child") and item.child )\ and item.child.render(style = style) or "" replacements["childlisting"] = child_txt #only LinkListing has a show_nums attribute if listing: if hasattr(listing, "show_nums"): if listing.show_nums: num_str = str(item.num) if hasattr(listing, "num_margin"): num_margin = str(listing.num_margin) else: num_margin = "%.2fex" % (len(str(listing.max_num)) * 1.1) else: num_str = '' num_margin = "0px;display:none" replacements["numcolmargin"] = num_margin replacements["num"] = num_str if hasattr(listing, "max_score"): mid_margin = len(str(listing.max_score)) if hasattr(listing, "mid_margin"): mid_margin = str(listing.mid_margin) elif mid_margin == 1: mid_margin = "15px" else: mid_margin = "%dex" % (mid_margin + 1) replacements["midcolmargin"] = mid_margin #$votehash is only present when voting arrows are present if c.user_is_loggedin: replacements['votehash'] = vote_hash(c.user, item, listing.vote_hash_type) if hasattr(item, "num_comments"): if not item.num_comments: # generates "comment" the imperative verb com_label = _("comment {verb}") com_cls = 'comments empty' else: # generates "XX comments" as a noun com_label = ungettext("comment", "comments", item.num_comments) com_label = strings.number_label % dict(num=item.num_comments, thing=com_label) com_cls = 'comments' if style == "compact": com_label = unicode(item.num_comments) replacements['numcomments'] = com_label replacements['commentcls'] = com_cls replacements['display'] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub (replacements['scoredislikes'], replacements['scoreunvoted'], replacements['scorelikes']) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): if hasattr(item, "promoted") and item.promoted is not None: from r2.lib import promote # promoted links are special in their date handling replacements['timesince'] = timesince(item._date - promote.timezone_offset) else: replacements['timesince'] = timesince(item._date) replacements['time_period'] = calc_time_period(item._date) # Set in front.py:GET_comments() replacements['previous_visits_hex'] = c.previous_visits_hex renderer = render_func or item.render res = renderer(style=style, **replacements) if isinstance(res, (str, unicode)): rv = unsafe(res) if g.debug: for leftover in re.findall('<\$>(.+?)(?:<|$)', rv): print "replace_render didn't replace %s" % leftover return rv return res
def wrap_items(self, items): user = c.user if c.user_is_loggedin else None #get authors #TODO pull the author stuff into add_props for links and #comments and messages? try: aids = set(l.author_id for l in items) except AttributeError: aids = None authors = Account._byID(aids, True) if aids else {} # srids = set(l.sr_id for l in items if hasattr(l, "sr_id")) subreddits = Subreddit.load_subreddits(items) if not user: can_ban_set = set() else: can_ban_set = set(id for (id,sr) in subreddits.iteritems() if sr.can_ban(user)) #get likes/dislikes #TODO Vote.likes should accept empty lists likes = Vote.likes(user, items) if user and items else {} reports = Report.fastreported(user, items) if user else {} uid = user._id if user else None # we'll be grabbing this in the spam processing below if c.user_is_admin: ban_info = admintools.ban_info([x for x in items if x._spam]) elif user and len(can_ban_set) > 0: ban_info = admintools.ban_info( [ x for x in items if (x._spam and hasattr(x,'sr_id') and x.sr_id in can_ban_set) ]) else: ban_info = dict() types = {} wrapped = [] count = 0 for item in items: w = self.wrap(item) wrapped.append(w) types.setdefault(w.render_class, []).append(w) #TODO pull the author stuff into add_props for links and #comments and messages? try: w.author = authors.get(item.author_id) w.friend = item.author_id in user.friends if user else False except AttributeError: w.author = None w.friend = False if hasattr(item, "sr_id"): w.subreddit = subreddits[item.sr_id] vote = likes.get((user, item)) if vote: w.likes = (True if vote._name == '1' else False if vote._name == '-1' else None) else: w.likes = None #definite w.timesince = utils.timesince(item._date) # update vote tallies compute_votes(w, item) w.score = [w.upvotes, w.downvotes] w.deleted = item._deleted w.rowstyle = w.rowstyle if hasattr(w,'rowstyle') else '' w.rowstyle += ' ' + ('even' if (count % 2) else 'odd') count += 1 # would have called it "reported", but that is already # taken on the thing itself as "how many total # reports". Indicates whether this user reported this # item, and should be visible to all users w.report_made = reports.get((user, item, Report._field)) # if the user can ban things on a given subreddit, or an # admin, then allow them to see that the item is spam, and # add the other spam-related display attributes w.show_reports = False w.show_spam = False w.can_ban = False if (c.user_is_admin or (user and hasattr(item,'sr_id') and item.sr_id in can_ban_set)): w.can_ban = True if item._spam: w.show_spam = True if not hasattr(item,'moderator_banned'): w.moderator_banned = False w.autobanned, w.banner = ban_info.get(item._fullname, (False, None)) elif hasattr(item,'reported') and item.reported > 0: w.show_reports = True for cls in types.keys(): cls.add_props(user, types[cls]) return wrapped
def _replace_render(style=None, display=True): """ A helper function for listings to set uncachable attributes on a rendered thing (item) to its proper display values for the current context. """ style = style or c.render_style or "html" replacements = {} if hasattr(item, "child"): if item.child: replacements["childlisting"] = item.child.render(style=style) else: # Special case for when the comment tree wasn't built which # occurs both in the inbox and spam page view of comments. replacements["childlisting"] = None else: replacements["childlisting"] = "" # only LinkListing has a show_nums attribute if listing and hasattr(listing, "show_nums"): replacements["num"] = str(item.num) if listing.show_nums else "" if hasattr(item, "num_comments"): com_label, com_cls = comment_label(item.num_comments) if style == "compact": com_label = unicode(item.num_comments) replacements["numcomments"] = com_label replacements["commentcls"] = com_cls replacements["display"] = "" if display else "style='display:none'" if hasattr(item, "render_score"): # replace the score stub ( replacements["scoredislikes"], replacements["scoreunvoted"], replacements["scorelikes"], ) = item.render_score # compute the timesince here so we don't end up caching it if hasattr(item, "_date"): if hasattr(item, "promoted") and item.promoted is not None: from r2.lib import promote # promoted links are special in their date handling replacements["timesince"] = timesince(item._date - promote.timezone_offset) else: replacements["timesince"] = timesince(item._date) replacements["time_period"] = calc_time_period(item._date) # compute the last edited time here so we don't end up caching it if hasattr(item, "editted") and not isinstance(item.editted, bool): replacements["lastedited"] = timesince(item.editted) # Set in front.py:GET_comments() replacements["previous_visits_hex"] = c.previous_visits_hex renderer = render_func or item.render res = renderer(style=style, **replacements) if isinstance(res, (str, unicode)): rv = unsafe(res) if g.debug: for leftover in re.findall("<\$>(.+?)(?:<|$)", rv): print "replace_render didn't replace %s" % leftover return rv return res