diff --git a/r2/r2/config/routing.py b/r2/r2/config/routing.py index beb92f9b7..2fe26f742 100644 --- a/r2/r2/config/routing.py +++ b/r2/r2/config/routing.py @@ -159,6 +159,7 @@ def make_map(global_conf={}, app_conf={}): mc('/post/:action', controller='post', requirements=dict(action="options|over18|unlogged_options|optout|optin|login|reg")) + mc('/api/distinguish/:how', controller='api', action="distinguish") mc('/api/:action/:url_user', controller='api', requirements=dict(action="login|register")) mc('/api/gadget/click/:ids', controller = 'api', action='gadget', type='click') diff --git a/r2/r2/controllers/api.py b/r2/r2/controllers/api.py index 8595509eb..ced43ca60 100644 --- a/r2/r2/controllers/api.py +++ b/r2/r2/controllers/api.py @@ -566,6 +566,7 @@ class ApiController(RedditController): wrapper = default_thing_wrapper(expand_children = True) jquery(".content").replace_things(item, True, True, wrap = wrapper) + jquery(".content .link .rank").hide() @validatedForm(VUser(), VModhash(), @@ -1024,7 +1025,7 @@ class ApiController(RedditController): if redir: form.redirect(redir) - @noresponse(VModhash(), + @noresponse(VUser(), VModhash(), VSrCanBan('id'), thing = VByName('id')) def POST_ban(self, thing): @@ -1035,7 +1036,8 @@ class ApiController(RedditController): # NB: change table updated by reporting unreport(thing, correct=True, auto=False) - @noresponse(VModhash(), + + @noresponse(VUser(), VModhash(), VSrCanBan('id'), thing = VByName('id')) def POST_unban(self, thing): @@ -1043,7 +1045,7 @@ class ApiController(RedditController): if not thing: return unreport(thing, correct=False) - @noresponse(VModhash(), + @noresponse(VUser(), VModhash(), VSrCanBan('id'), thing = VByName('id')) def POST_ignore(self, thing): @@ -1051,6 +1053,19 @@ class ApiController(RedditController): # NB: change table updated by reporting unreport(thing, correct=False) + @validatedForm(VUser(), VModhash(), + VSrCanDistinguish('id'), + thing = VByName('id'), + how = VOneOf('how', ('yes','no','admin'))) + def POST_distinguish(self, form, jquery, thing, how): + if not thing:return + thing.distinguished = how + thing._commit() + wrapper = default_thing_wrapper(expand_children = True) + w = wrap_links(thing, wrapper) + jquery(".content").replace_things(w, True, True) + jquery(".content .link .rank").hide() + @noresponse(VUser(), VModhash(), thing = VByName('id')) diff --git a/r2/r2/controllers/validator/validator.py b/r2/r2/controllers/validator/validator.py index f1c4e4eb5..58cea9fd7 100644 --- a/r2/r2/controllers/validator/validator.py +++ b/r2/r2/controllers/validator/validator.py @@ -436,6 +436,21 @@ class VSrModerator(Validator): or c.user_is_admin): abort(403, "forbidden") +class VSrCanDistinguish(VByName): + def run(self, thing_name): + if c.user_is_admin: + return True + elif c.user_is_loggedin: + item = VByName.run(self, thing_name) + if item.author_id == c.user._id: + # will throw a legitimate 500 if this isn't a link or + # comment, because this should only be used on links and + # comments + subreddit = item.subreddit_slow + if subreddit.can_distinguish(c.user): + return True + abort(403,'forbidden') + class VSrCanBan(VByName): def run(self, thing_name): if c.user_is_admin: diff --git a/r2/r2/lib/pages/things.py b/r2/r2/lib/pages/things.py index 5f6b98d00..5784ebd45 100644 --- a/r2/r2/lib/pages/things.py +++ b/r2/r2/lib/pages/things.py @@ -27,26 +27,27 @@ from r2.lib.strings import Score from pylons import c class PrintableButtons(Styled): - def __init__(self, style, thing, show_ban = True, - show_delete = False, show_report = True, **kw): + def __init__(self, style, thing, + show_delete = False, show_report = True, + show_distinguish = False, **kw): show_report = show_report and c.user_is_loggedin Styled.__init__(self, style = style, - fullnaem = thing._fullname, + fullname = thing._fullname, can_ban = thing.can_ban, show_spam = thing.show_spam, show_reports = thing.show_reports, - show_ban = show_ban, show_delete = show_delete, - show_report = show_report, **kw) + show_report = show_report, + show_distinguish = show_distinguish, + **kw) class BanButtons(PrintableButtons): - def __init__(self, thing, show_ban = True, + def __init__(self, thing, show_delete = False, show_report = True): PrintableButtons.__init__(self, "banbuttons", thing) class LinkButtons(PrintableButtons): - def __init__(self, thing, comments = True, delete = True, report = True, - ban = True): + def __init__(self, thing, comments = True, delete = True, report = True): # is the current user the author? is_author = (c.user_is_loggedin and thing.author and c.user.name == thing.author.name) @@ -54,6 +55,11 @@ class LinkButtons(PrintableButtons): show_report = not is_author and report # do we show the delete button? show_delete = is_author and delete and not thing._deleted + # do we show the distinguish button? among other things, + # we never want it to appear on link listings -- only + # comments pages + show_distinguish = (is_author and thing.can_ban + and getattr(thing, "expand_children", False)) PrintableButtons.__init__(self, 'linkbuttons', thing, # user existence and preferences @@ -69,7 +75,7 @@ class LinkButtons(PrintableButtons): hidden = thing.hidden, show_delete = show_delete, show_report = show_report, - show_ban = ban, + show_distinguish = show_distinguish, show_comments = comments) class CommentButtons(PrintableButtons): @@ -82,6 +88,8 @@ class CommentButtons(PrintableButtons): # do we show the delete button? show_delete = is_author and delete and not thing._deleted + show_distinguish = is_author and thing.can_ban + PrintableButtons.__init__(self, "commentbuttons", thing, is_author = is_author, profilepage = c.profilepage, @@ -90,8 +98,8 @@ class CommentButtons(PrintableButtons): parent_permalink = thing.parent_permalink, can_reply = thing.can_reply, show_report = show_report, - show_delete = show_delete, - show_ban = True) + show_distinguish = show_distinguish, + show_delete = show_delete) class MessageButtons(PrintableButtons): def __init__(self, thing, delete = False, report = True): @@ -104,8 +112,7 @@ class MessageButtons(PrintableButtons): was_comment = was_comment, can_reply = c.user_is_loggedin, show_report = True, - show_delete = False, - show_ban = True) + show_delete = False) # formerly ListingController.builder_wrapper def default_thing_wrapper(**params): diff --git a/r2/r2/lib/template_helpers.py b/r2/r2/lib/template_helpers.py index bb04dc388..ce3d44e6e 100644 --- a/r2/r2/lib/template_helpers.py +++ b/r2/r2/lib/template_helpers.py @@ -318,9 +318,9 @@ def find_author_class(thing, attribs, gray): if gray: author_cls += " gray" - elif attribs: - #the last element in attribs will be the highest priority - extra_class = attribs[-1][2] - author_cls += " " + extra_class + + for priority, abbv, css_class, label, attr_link in attribs: + author_cls += " " + css_class + return author_cls diff --git a/r2/r2/models/builder.py b/r2/r2/models/builder.py index bbad6f640..11694ba94 100644 --- a/r2/r2/models/builder.py +++ b/r2/r2/models/builder.py @@ -56,20 +56,27 @@ def add_attr(attrs, code, label=None, link=None): label = _('friend') if not link: link = '/prefs/friends' - elif code == 'M': - priority = 2 - cssclass = 'moderator' - if not label: - raise ValueError ("Need a label") - if not link: - raise ValueError ("Need a link") elif code == 'S': - priority = 3 + priority = 2 cssclass = 'submitter' if not label: label = _('submitter') if not link: raise ValueError ("Need a link") + elif code == 'M': + priority = 3 + cssclass = 'moderator' + if not label: + raise ValueError ("Need a label") + if not link: + raise ValueError ("Need a link") + elif code == 'A': + priority = 4 + cssclass = 'admin' + if not label: + label = _('reddit admin, speaking officially') + if not link: + link = '/help/faq#Whomadereddit' else: raise ValueError ("Got weird code [%s]" % code) @@ -140,7 +147,7 @@ class Builder(object): else: modlink = '/r/%s/about/moderators' % c.site.name - modlabel = (_('moderator of /r/%(reddit)s') % + modlabel = (_('moderator of /r/%(reddit)s, speaking officially') % dict(reddit = c.site.name) ) @@ -161,6 +168,13 @@ class Builder(object): # css class, i18n'ed mouseover label, hyperlink (or None)> w.attribs = [] + w.distinguished = None + if hasattr(item, "distinguished"): + if item.distinguished == 'yes': + w.distinguished = 'moderator' + elif item.distinguished == 'admin': + w.distinguished = 'admin' + try: w.author = authors.get(item.author_id) if user and item.author_id in user.friends: @@ -172,7 +186,12 @@ class Builder(object): except AttributeError: pass - if getattr(item, "author_id", None) in mods: + if (w.distinguished == 'admin' and + w.author and w.author.name in g.admins): + add_attr(w.attribs, 'A') + + if (w.distinguished == 'moderator' and + getattr(item, "author_id", None) in mods): add_attr(w.attribs, 'M', label=modlabel, link=modlink) if hasattr(item, "sr_id"): diff --git a/r2/r2/models/subreddit.py b/r2/r2/models/subreddit.py index 018aca1f7..699a7cd67 100644 --- a/r2/r2/models/subreddit.py +++ b/r2/r2/models/subreddit.py @@ -173,6 +173,11 @@ class Subreddit(Thing, Printable): and (c.user_is_admin or self.is_moderator(user))) + def can_distinguish(self,user): + return (user + and (c.user_is_admin + or self.is_moderator(user))) + def can_change_stylesheet(self, user): if c.user_is_loggedin: return c.user_is_admin or self.is_moderator(user) diff --git a/r2/r2/public/static/css/reddit.css b/r2/r2/public/static/css/reddit.css index 986857f4e..7cb079319 100644 --- a/r2/r2/public/static/css/reddit.css +++ b/r2/r2/public/static/css/reddit.css @@ -485,7 +485,9 @@ before enabling */ .tagline a {color: #369; text-decoration: none; } .tagline .friend { color: orangered } .tagline .submitter { color: #0055df } -.tagline .moderator { color: #338833 } +.tagline .moderator { color: #228822 } +.tagline .admin { color: #ff0000; } +.tagline a.author.admin { font-weight: bold } .tagline a:hover { text-decoration: underline } .media-button .option { color: red; } @@ -689,6 +691,11 @@ a.star { text-decoration: none; color: #ff8b60 } padding: 0 1px; } +.entry .buttons li a.nonbutton { + color: #369; + font-weight: normal; +} + .entry .buttons a:hover {text-decoration: underline} /* links */ diff --git a/r2/r2/public/static/js/reddit.js b/r2/r2/public/static/js/reddit.js index b0eb09e18..97ff14de7 100644 --- a/r2/r2/public/static/js/reddit.js +++ b/r2/r2/public/static/js/reddit.js @@ -298,7 +298,6 @@ function cancelShare(elem) { return cancelToggleForm(elem, ".sharelink", ".share-button"); }; - /* Comment generation */ function helpon(elem) { $(elem).parents(".usertext-edit:first").children(".markhelp:first").show(); @@ -899,6 +898,16 @@ function reply(elem) { form.find(".cancel").get(0).onclick = function() {form.hide()}; } +function toggle_distinguish_span(elem) { + var form = $(elem).parents("form")[0]; + $(form).children().toggle(); +} + +function set_distinguish(elem, value) { + change_state(elem, "distinguish/" + value); + $(elem).children().toggle(); +} + function populate_click_gadget() { /* if we can find the click-gadget, populate it */ if($('.click-gadget').length) { diff --git a/r2/r2/templates/link.html b/r2/r2/templates/link.html index 9bc80a427..b1cdaa244 100644 --- a/r2/r2/templates/link.html +++ b/r2/r2/templates/link.html @@ -181,9 +181,9 @@ <%def name="child()"> -<%def name="buttons(comments=True, delete=True, report=True, ban=True, additional='')"> +<%def name="buttons(comments=True, delete=True, report=True, additional='')"> ${LinkButtons(thing, comments = comments, delete = delete, - report = report, ban = ban)} + report = report)} <%def name="thumbnail()"> diff --git a/r2/r2/templates/printable.html b/r2/r2/templates/printable.html index 2a822cd2c..82c9a5044 100644 --- a/r2/r2/templates/printable.html +++ b/r2/r2/templates/printable.html @@ -97,7 +97,7 @@ thing id-${what._fullname} <%def name="buttons(ban=True)"> - ${BanButtons(thing, show_ban = ban)} + ${BanButtons(thing)} <%def name="ParentDiv()"> @@ -144,14 +144,13 @@ thing id-${what._fullname} ${abbv} %else: ${abbv} %endif - ## author_cls contains the final css_class, so this is a hack - ## that will print a comma after all but the final attr - %if css_class not in author_cls: + ## this is a hack to print a comma after all but the final attr + %if priority != attribs[-1][0]: , %endif %endfor diff --git a/r2/r2/templates/printablebuttons.html b/r2/r2/templates/printablebuttons.html index ba4363845..c71ffd3f9 100644 --- a/r2/r2/templates/printablebuttons.html +++ b/r2/r2/templates/printablebuttons.html @@ -35,7 +35,7 @@ ${ynbutton(_("delete"), _("deleted"), "del", "hide_thing")} %endif - %if thing.can_ban and thing.show_ban: + %if thing.can_ban: %if thing.show_spam:
  • ${self.state_button("unban", _("unban"), @@ -56,6 +56,44 @@ %endif +<%def name="distinguish_setter(value)"> + ${_(value)} + + +<%def name="distinguish()"> + %if thing.show_distinguish: +
  • +
    + + ${_("distinguish")} + + ${_("distinguish this?")} + + + ${distinguish_setter('yes')} + / + ${distinguish_setter('no')} + + + %if c.user_is_admin: + / + ${distinguish_setter('admin')} + + %endif + + / + + help + + + +
    +
  • + %endif + + <%def name="linkbuttons()"> %if thing.show_comments:
  • @@ -93,6 +131,7 @@
  • %endif ${self.banbuttons()} + ${self.distinguish()} <%def name="commentbuttons()"> @@ -119,9 +158,10 @@ %endif %endif ${self.banbuttons()} + ${self.distinguish()} %if not thing.profilepage and thing.can_reply:
  • - ${self.simple_button(_("reply"), "reply")} + ${self.simple_button(_("reply {verb}"), "reply")}
  • %endif %endif diff --git a/r2/r2/templates/promotedlink.html b/r2/r2/templates/promotedlink.html index 38b0bf442..48bafd2f1 100644 --- a/r2/r2/templates/promotedlink.html +++ b/r2/r2/templates/promotedlink.html @@ -45,7 +45,6 @@ <%def name="buttons()"> ${parent.buttons(comments=not thing.disable_comments, report=False, - ban=False, additional=unpromote_button())}