From b3bb35131e8f0ed14fb4b9e64c7ddafa8fdc391e Mon Sep 17 00:00:00 2001 From: Neil Williams Date: Sat, 6 Oct 2012 10:47:37 -0700 Subject: [PATCH] gold: New feature: Comment gilding. This feature adds a "give gold" button beneath each comment. You can give a month of gold to the user who created the comment. An icon is added to comments that've inspired gold gifting. --- r2/r2/controllers/api.py | 31 ++++++ r2/r2/controllers/front.py | 41 +++++--- r2/r2/controllers/ipn.py | 55 +++++++++-- r2/r2/lib/js.py | 1 + r2/r2/lib/pages/pages.py | 37 ++++++-- r2/r2/lib/pages/things.py | 18 ++++ r2/r2/lib/strings.py | 2 + r2/r2/models/link.py | 75 ++++++++++++++- r2/r2/models/subreddit.py | 1 + r2/r2/public/static/css/reddit.css | 50 ++++++++-- r2/r2/public/static/js/base.js | 1 + r2/r2/public/static/js/gold.js | 92 ++++++++++++++++++ r2/r2/templates/comment.html | 5 + r2/r2/templates/goldpayment.html | 132 +++++++++++++------------- r2/r2/templates/printable.html | 6 ++ r2/r2/templates/printablebuttons.html | 8 ++ r2/r2/templates/thingupdater.html | 5 + 17 files changed, 457 insertions(+), 103 deletions(-) create mode 100644 r2/r2/public/static/js/gold.js diff --git a/r2/r2/controllers/api.py b/r2/r2/controllers/api.py index 4b8ddef59..fc7a4fb63 100755 --- a/r2/r2/controllers/api.py +++ b/r2/r2/controllers/api.py @@ -64,6 +64,7 @@ from r2.controllers.api_docs import api_doc, api_section from r2.lib.search import SearchQuery from r2.controllers.oauth2 import OAuth2ResourceController, require_oauth2_scope from r2.lib.system_messages import notify_user_added +from r2.controllers.ipn import generate_blob from r2.models import wiki from r2.lib.merge import ConflictException @@ -3060,3 +3061,33 @@ class ApiController(RedditController, OAuth2ResourceController): % client._id).hide() jquery('#developed-app-%s .edit-app-icon-button' % client._id).toggleClass('collapsed') + + @json_validate(VUser(), + VModhash(), + comment=VByName("comment", thing_cls=Comment)) + def POST_generate_payment_blob(self, responder, comment): + if not comment: + abort(400, "Bad Request") + + comment_sr = Subreddit._byID(comment.sr_id, data=True) + if not comment_sr.allow_comment_gilding: + abort(403, "Forbidden") + + try: + recipient = Account._byID(comment.author_id, data=True) + except NotFound: + self.abort404() + + if recipient._deleted: + self.abort404() + + return generate_blob(dict( + goldtype="gift", + account_id=c.user._id, + account_name=c.user.name, + status="initialized", + signed=False, + recipient=recipient.name, + giftmessage=None, + comment=comment._fullname, + )) diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py index 2c6378902..e94911fa2 100755 --- a/r2/r2/controllers/front.py +++ b/r2/r2/controllers/front.py @@ -33,7 +33,6 @@ from r2.lib.pages import trafficpages from r2.lib.menus import * from r2.lib.utils import to36, sanitize_url, check_cheating, title_to_url from r2.lib.utils import query_string, UrlParser, link_from_url, link_duplicates -from r2.lib.utils import randstr from r2.lib.template_helpers import get_domain from r2.lib.filters import unsafe, _force_unicode from r2.lib.emailer import has_opted_out, Email @@ -52,6 +51,7 @@ from oauth2 import OAuth2ResourceController, require_oauth2_scope from api_docs import api_doc, api_section from pylons import c, request, request, Response from r2.models.token import EmailVerificationToken +from r2.controllers.ipn import generate_blob from operator import attrgetter import string @@ -1268,9 +1268,16 @@ class FormsController(RedditController): # variables below are just for gifts signed = VBoolean("signed"), recipient_name = VPrintable("recipient", max_length = 50), + comment = VByName("comment", thing_cls=Comment), giftmessage = VLength("giftmessage", 10000)) def GET_gold(self, goldtype, period, months, - signed, recipient_name, giftmessage): + signed, recipient_name, giftmessage, comment): + + if comment: + comment_sr = Subreddit._byID(comment.sr_id, data=True) + if comment._deleted or not comment_sr.allow_comment_gilding: + comment = None + start_over = False recipient = None if goldtype == "autorenew": @@ -1282,10 +1289,18 @@ class FormsController(RedditController): elif goldtype == "gift": if months is None or months < 1: start_over = True - try: - recipient = Account._by_name(recipient_name or "") - except NotFound: - start_over = True + + if comment: + recipient = Account._byID(comment.author_id, data=True) + if recipient._deleted: + comment = None + recipient = None + start_over = True + else: + try: + recipient = Account._by_name(recipient_name or "") + except NotFound: + start_over = True else: goldtype = "" start_over = True @@ -1303,19 +1318,17 @@ class FormsController(RedditController): if goldtype == "gift": payment_blob["signed"] = signed - payment_blob["recipient"] = recipient_name + payment_blob["recipient"] = recipient.name payment_blob["giftmessage"] = giftmessage + if comment: + payment_blob["comment"] = comment._fullname - passthrough = randstr(15) - - g.hardcache.set("payment_blob-" + passthrough, - payment_blob, 86400 * 30) - - g.log.info("just set payment_blob-%s" % passthrough) + passthrough = generate_blob(payment_blob) return BoringPage(_("reddit gold"), show_sidebar=False, content=GoldPayment(goldtype, period, months, signed, recipient, - giftmessage, passthrough) + giftmessage, passthrough, + comment) ).render() diff --git a/r2/r2/controllers/ipn.py b/r2/r2/controllers/ipn.py index a39c7152d..e9b46af4e 100644 --- a/r2/r2/controllers/ipn.py +++ b/r2/r2/controllers/ipn.py @@ -31,9 +31,20 @@ from pylons.i18n import _ from validator import * from r2.models import * +from r2.lib.utils import randstr from reddit_base import RedditController + +def generate_blob(data): + passthrough = randstr(15) + + g.hardcache.set("payment_blob-" + passthrough, + data, 86400 * 30) + g.log.info("just set payment_blob-%s", passthrough) + return passthrough + + def get_blob(code): key = "payment_blob-" + code with g.make_lock("payment_blob", "payment_blob_lock-" + code): @@ -188,8 +199,15 @@ def months_and_days_from_pennies(pennies): days = 31 * months return (months, days) -def send_gift(buyer, recipient, months, days, signed, giftmessage): +def send_gift(buyer, recipient, months, days, signed, giftmessage, comment_id): admintools.engolden(recipient, days) + + if comment_id: + comment = Thing._by_fullname(comment_id, data=True) + comment._gild(buyer) + else: + comment = None + if signed: sender = buyer.name md_sender = "[%s](/user/%s)" % (sender, sender) @@ -198,20 +216,27 @@ def send_gift(buyer, recipient, months, days, signed, giftmessage): md_sender = "An anonymous redditor" create_gift_gold (buyer._id, recipient._id, days, c.start_time, signed) + if months == 1: amount = "a month" else: amount = "%d months" % months + if not comment: + message = strings.youve_got_gold % dict(sender=md_sender, amount=amount) + + if giftmessage and giftmessage.strip(): + message += "\n\n" + strings.giftgold_note + giftmessage + else: + message = strings.youve_got_comment_gold % dict( + url=comment.make_permalink_slow(), + ) + subject = sender + " just sent you reddit gold!" - message = strings.youve_got_gold % dict(sender=md_sender, amount=amount) - - if giftmessage and giftmessage.strip(): - message += "\n\n" + strings.giftgold_note + giftmessage - send_system_message(recipient, subject, message) g.log.info("%s gifted %s to %s" % (buyer.name, amount, recipient.name)) + return comment def _google_ordernum_request(ordernums): d = Document() @@ -290,6 +315,10 @@ class IpnController(RedditController): raise ValueError("Invalid username %s in spendcreddits, buyer = %s" % (recipient_name, c.user.name)) + if recipient._deleted: + form.set_html(".status", _("that user has deleted their account")) + return + if not c.user_is_admin: if months > c.user.gold_creddits: raise ValueError("%s is trying to sneak around the creddit check" @@ -299,7 +328,9 @@ class IpnController(RedditController): c.user.gold_creddit_escrow += months c.user._commit() - send_gift(c.user, recipient, months, days, signed, giftmessage) + comment_id = payment_blob.get("comment") + comment = send_gift(c.user, recipient, months, days, signed, + giftmessage, comment_id) if not c.user_is_admin: c.user.gold_creddit_escrow -= months @@ -309,7 +340,12 @@ class IpnController(RedditController): g.hardcache.set(blob_key, payment_blob, 86400 * 30) form.set_html(".status", _("the gold has been delivered!")) - jquery("button").hide() + form.find("button").hide() + + if comment: + gilding_message = make_comment_gold_message(comment, + user_gilded=True) + jquery.gild_comment(comment_id, gilding_message) @textresponse(full_sn = VLength('serial-number', 100)) def POST_gcheckout(self, full_sn): @@ -500,7 +536,8 @@ class IpnController(RedditController): % (recipient_name, custom)) signed = payment_blob.get("signed", False) giftmessage = payment_blob.get("giftmessage", False) - send_gift(buyer, recipient, months, days, signed, giftmessage) + comment_id = payment_blob.get("comment") + send_gift(buyer, recipient, months, days, signed, giftmessage, comment_id) instagift = True subject = _("thanks for giving reddit gold!") message = _("Your gift to %s has been delivered." % recipient.name) diff --git a/r2/r2/lib/js.py b/r2/r2/lib/js.py index aeeb89fe0..533927c9f 100755 --- a/r2/r2/lib/js.py +++ b/r2/r2/lib/js.py @@ -286,6 +286,7 @@ module["reddit"] = LocalizedModule("reddit.js", "wiki.js", "reddit.js", "apps.js", + "gold.js", ) module["mobile"] = LocalizedModule("mobile.js", diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index fc4dad480..8fff69989 100755 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -186,7 +186,19 @@ class Reddit(Templated): self.srtopbar = SubredditTopBar() if c.user_is_loggedin and self.show_sidebar and not is_api(): - self._content = PaneStack([ShareLink(), content]) + # insert some form templates for js to use + # TODO: move these to client side templates + gold = GoldPayment("gift", + "monthly", + months=1, + signed=False, + recipient="", + giftmessage=None, + passthrough=None, + comment=True, + clone_template=True, + ) + self._content = PaneStack([ShareLink(), content, gold]) else: self._content = content @@ -1164,6 +1176,7 @@ class CommentPane(Templated): dislikes = [] is_friend = set() saves = set() + gildings = {} for t in self.listing_iter(my_listing): if not hasattr(t, "likes"): # this is for MoreComments and MoreRecursion @@ -1174,11 +1187,14 @@ class CommentPane(Templated): likes.append(t._fullname) if t.likes is False: dislikes.append(t._fullname) + if t.user_gilded: + gildings[t._fullname] = t.gilded_message if t.saved: saves.add(t._fullname) self.rendered += ThingUpdater(likes = likes, dislikes = dislikes, is_friend = is_friend, + gildings = gildings, saves = saves).render() g.log.debug("using comment page cache") else: @@ -1704,7 +1720,8 @@ class Gold(Templated): class GoldPayment(Templated): def __init__(self, goldtype, period, months, signed, - recipient, giftmessage, passthrough): + recipient, giftmessage, passthrough, comment, + clone_template=False): pay_from_creddits = False if period == "monthly" or 1 <= months < 12: @@ -1751,7 +1768,9 @@ class GoldPayment(Templated): summary = strings.gold_summary_creddits % dict( amount=Score.somethings(months, "month")) elif goldtype == "gift": - if signed: + if comment: + format = strings.gold_summary_comment_gift + elif signed: format = strings.gold_summary_signed_gift else: format = strings.gold_summary_anonymous_gift @@ -1763,9 +1782,14 @@ class GoldPayment(Templated): # buy by month or spend a multiple of 12 months months = quantity * 12 - summary = format % dict( - amount=Score.somethings(months, "month"), - recipient = recipient.name) + if not clone_template: + summary = format % dict( + amount=Score.somethings(months, "month"), + recipient=recipient and recipient.name, + ) + else: + # leave the replacements to javascript + summary = format else: raise ValueError("wtf is %r" % goldtype) @@ -1777,6 +1801,7 @@ class GoldPayment(Templated): pay_from_creddits=pay_from_creddits, passthrough=passthrough, google_id=google_id, + comment=comment, clone_template=clone_template, paypal_buttonid=paypal_buttonid) class GiftGold(Templated): diff --git a/r2/r2/lib/pages/things.py b/r2/r2/lib/pages/things.py index eaa3e717b..bff450921 100644 --- a/r2/r2/lib/pages/things.py +++ b/r2/r2/lib/pages/things.py @@ -143,6 +143,23 @@ class CommentButtons(PrintableButtons): # do we show the delete button? show_delete = is_author and delete and not thing._deleted + can_gild = ( + # you can't gild your own comment + not is_author + # no point in showing the button for things you've already gilded + and not thing.user_gilded + # this is a way of checking if the user is logged in that works + # both within CommentPane instances and without. e.g. CommentPane + # explicitly sets user_is_loggedin = False but can_reply is + # correct. while on user overviews, you can't reply but will get + # the correct value for user_is_loggedin + and (c.user_is_loggedin or thing.can_reply) + # ick, if the author deleted their account we shouldn't waste gold + and not thing.author._deleted + # some subreddits can have gilding disabled + and thing.subreddit.allow_comment_gilding + ) + show_distinguish = is_author and (thing.can_ban or c.user_special_distinguish) PrintableButtons.__init__(self, "commentbuttons", thing, @@ -155,6 +172,7 @@ class CommentButtons(PrintableButtons): deleted = thing.deleted, parent_permalink = thing.parent_permalink, can_reply = thing.can_reply, + can_gild=can_gild, show_report = show_report, show_distinguish = show_distinguish, show_delete = show_delete) diff --git a/r2/r2/lib/strings.py b/r2/r2/lib/strings.py index 795ca66ae..be3add15d 100644 --- a/r2/r2/lib/strings.py +++ b/r2/r2/lib/strings.py @@ -145,11 +145,13 @@ string_dict = dict( over_comment_limit_gold = _("Sorry, the maximum number of comments is %d."), youve_got_gold = _("%(sender)s just sent you %(amount)s of reddit gold! Wasn't that nice?"), giftgold_note = _("Here's a note that was included:\n\n----\n\n"), + youve_got_comment_gold = _("A redditor liked [your comment](%(url)s) so much, they gave you a month of reddit gold. Shiny!"), gold_summary_autorenew = _("You're about to set up an ongoing, autorenewing subscription to reddit gold for yourself (%(user)s)."), gold_summary_onetime = _("You're about to make a one-time purchase of %(amount)s of reddit gold for yourself (%(user)s)."), gold_summary_creddits = _("You're about to purchase %(amount)s of reddit gold creddits. They work like gift certificates: each creddit you have will allow you to give one month of reddit gold to someone else."), gold_summary_signed_gift = _("You're about to give %(amount)s of reddit gold to %(recipient)s, who will be told that it came from you."), gold_summary_anonymous_gift = _("You're about to give %(amount)s of reddit gold to %(recipient)s. It will be an anonymous gift."), + gold_summary_comment_gift = _("Want to say thanks to *%(recipient)s* for this comment? Give them a month of [reddit gold](/help/gold)."), unvotable_message = _("sorry, this has been archived and can no longer be voted on"), account_activity_blurb = _("This page shows a history of recent activity on your account. If you notice unusual activity, you should change your password immediately. Location information is guessed from your computer's IP address and may be wildly wrong, especially for visits from mobile devices. Note: due to a bug, private-use addresses (starting with 10.) sometimes show up erroneously in this list after regular use of the site."), your_current_ip_is = _("You are currently accessing reddit from this IP address: %(address)s."), diff --git a/r2/r2/models/link.py b/r2/r2/models/link.py index becad21e9..dda60d117 100755 --- a/r2/r2/models/link.py +++ b/r2/r2/models/link.py @@ -618,12 +618,53 @@ class PromotedLink(Link): # Run this last Printable.add_props(user, wrapped) + +def make_comment_gold_message(comment, user_gilded): + author = Account._byID(comment.author_id, data=True) + if not comment._deleted and not author._deleted: + author_name = author.name + else: + author_name = _("[deleted]") + + if comment.gildings == 0: + return None + + if c.user_is_loggedin and comment.author_id == c.user._id: + gilded_message = ungettext( + "a redditor gifted you a month of reddit gold for this comment.", + "redditors have gifted you %(months)d months of reddit gold for " + "this comment.", + comment.gildings + ) + elif user_gilded: + gilded_message = ungettext( + "you have gifted reddit gold to %(recipient)s for this comment.", + "you and other redditors have gifted %(months)d months of " + "reddit gold to %(recipient)s for this comment.", + comment.gildings + ) + else: + gilded_message = ungettext( + "a redditor has gifted reddit gold to %(recipient)s for this " + "comment.", + "redditors have gifted %(months)d months of reddit gold to " + "%(recipient)s for this comment.", + comment.gildings + ) + + return gilded_message % dict( + recipient=author_name, + months=comment.gildings, + ) + + class Comment(Thing, Printable): - _data_int_props = Thing._data_int_props + ('reported',) + _data_int_props = Thing._data_int_props + ('reported', 'gildings') _defaults = dict(reported=0, parent_id=None, moderator_banned=False, new=False, + gildings=0, banned_before_moderator=False) _essentials = ('link_id', 'author_id') @@ -722,6 +763,10 @@ class Comment(Thing, Printable): return self.make_permalink(l, l.subreddit_slow, context=context, anchor=anchor) + def _gild(self, user): + self._incr("gildings") + GildedCommentsByAccount.gild_comment(user, self) + @classmethod def add_props(cls, user, wrapped): from r2.lib.template_helpers import add_attr, get_domain @@ -765,12 +810,21 @@ class Comment(Thing, Printable): site = c.site if user_is_loggedin: + gilded = [comment for comment in wrapped if comment.gildings > 0] + try: + user_gildings = GildedCommentsByAccount.fast_query(user, + gilded) + except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: + g.log.warning("Cassandra gilding lookup failed: %r", e) + user_gildings = {} + try: saved = CommentSavesByAccount.fast_query(user, wrapped) except tdb_cassandra.TRANSIENT_EXCEPTIONS as e: g.log.warning("Cassandra comment save lookup failed: %r", e) saved = {} else: + user_gildings = {} saved = {} for item in wrapped: @@ -807,9 +861,13 @@ class Comment(Thing, Printable): item.can_reply = True if user_is_loggedin: + item.user_gilded = (user, item) in user_gildings item.saved = (user, item) in saved else: + item.user_gilded = False item.saved = False + item.gilded_message = make_comment_gold_message(item, + item.user_gilded) # not deleted on profile pages, # deleted if spam and not author or admin @@ -1256,6 +1314,21 @@ class SaveHide(Relation(Account, Link)): pass class Click(Relation(Account, Link)): pass + +class GildedCommentsByAccount(tdb_cassandra.DenormalizedRelation): + _use_db = True + _last_modified_name = 'Gilding' + _views = [] + + @classmethod + def value_for(cls, thing1, thing2, opaque): + return '' + + @classmethod + def gild_comment(cls, user, comment): + cls.create(user, [comment]) + + class _SaveHideByAccount(tdb_cassandra.DenormalizedRelation): @classmethod def value_for(cls, thing1, thing2, opaque): diff --git a/r2/r2/models/subreddit.py b/r2/r2/models/subreddit.py index 61e58b495..d37c6a91f 100644 --- a/r2/r2/models/subreddit.py +++ b/r2/r2/models/subreddit.py @@ -97,6 +97,7 @@ class Subreddit(Thing, Printable): public_description = "", prev_description_id = "", prev_public_description_id = "", + allow_comment_gilding=True, ) _essentials = ('type', 'name', 'lang') _data_int_props = Thing._data_int_props + ('mod_actions', 'reported') diff --git a/r2/r2/public/static/css/reddit.css b/r2/r2/public/static/css/reddit.css index 7827eb410..c678a6c10 100755 --- a/r2/r2/public/static/css/reddit.css +++ b/r2/r2/public/static/css/reddit.css @@ -5347,8 +5347,8 @@ tr.gold-accent + tr > td { background-image: url(../giftgold.png); /* SPRITE */ } -.gold-form h1:before { - margin-top: 3px; +.gold-form p:first-child { + margin: 0 0 8px 0; } .tiny { @@ -5374,11 +5374,6 @@ tr.gold-accent + tr > td { color: #6a4d00; } -.gold-form .roundfield-content .gray a { - margin-left: 0.8em; - font-size: small; -} - .gold-form .note { font-size: 11px; font-style: italic; @@ -5395,13 +5390,30 @@ tr.gold-accent + tr > td { position: absolute; } +.gold-form.cloneable { + display: none; +} + .gold-form textarea, .gold-form input[type=text] { margin-top: 3px; } +.gold-payment form { + display: inline; +} + .gold-logo { float: left; - margin-right: 15px; + margin: 5px 0; +} + +.comment .gold-form { + margin: 0 0 10px 4px; + min-height: 0; +} + +.gold-payment .roundfield-content { + margin-left: 80px; } .giftmessage { @@ -5427,6 +5439,13 @@ tr.gold-accent + tr > td { text-shadow: 0px 1px 0px hsla(0,0%,100%,.7); } +.gold-button.disabled { + color: #999; + background-color: #ccc; + border-color: #aaa; + text-shadow: none; +} + .gold-dropdown { color: #482800; background-color: #fff088; @@ -6253,6 +6272,21 @@ table.diff {font-size: small;} .diff_chg {background-color:yellow} .diff_sub {background-color:lightcoral} +span.gilded-comment-icon { + display: inline-block; + background-image: url(../gold-coin.png); /* SPRITE */ + height: 14px; + width: 14px; + margin: 0 0 -3px 8px; +} + +.user-gilded > .entry span.gilded-comment-icon { + background-image: url(../gold-coin-yours.png); /* SPRITE */ + height: 24px; + width: 24px; + margin-bottom: -8px; +} + .buttons li.comment-save-button { display: none; } .buttons li.comment-unsave-button { display: inline; } diff --git a/r2/r2/public/static/js/base.js b/r2/r2/public/static/js/base.js index 39e22eb38..02dc8d7f1 100644 --- a/r2/r2/public/static/js/base.js +++ b/r2/r2/public/static/js/base.js @@ -16,4 +16,5 @@ $(function() { r.interestbar.init() r.apps.init() r.wiki.init() + r.gold.init() }) diff --git a/r2/r2/public/static/js/gold.js b/r2/r2/public/static/js/gold.js new file mode 100644 index 000000000..195610cd7 --- /dev/null +++ b/r2/r2/public/static/js/gold.js @@ -0,0 +1,92 @@ +r.gold = { + _googleCheckoutAnalyticsLoaded: false, + + init: function () { + $('div.content').on( + 'click', + 'a.give-gold, .gilded-comment-icon', + $.proxy(this, '_toggleCommentGoldForm') + ) + }, + + _toggleCommentGoldForm: function (e) { + var $link = $(e.target), + commentId = $link.thing_id(), + formId = 'gold_form_' + commentId, + oldForm = $('#' + formId) + + if ($link.thing().hasClass('user-gilded') || + $link.thing().hasClass('deleted')) { + return false + } + + if (oldForm.length) { + oldForm.toggle() + return false + } + + if (!this._googleCheckoutAnalyticsLoaded) { + // we're just gonna hope this loads fast enough since there's no + // way to know if it failed and we'd rather the form is still + // usable if things don't go well with the analytics stuff. + $.getScript('//checkout.google.com/files/digital/ga_post.js') + this._googleCheckoutAnalyticsLoaded = true + } + + var form = $('.gold-form.cloneable:first').clone(), + authorName = $link.thing().find('.entry .author:first').text(), + message = r.strings.gold_summary_comment_gift.replace('%(recipient)s', authorName), + passthroughs = form.find('.passthrough') + + form.removeClass('cloneable') + .attr('id', formId) + .find('p:first-child em').text(authorName).end() + .find('button').attr('disabled', '') + passthroughs.val('') + $link.new_thing_child(form) + form.show() + + // show the throbber if this takes longer than 200ms + var workingTimer = setTimeout(function () { + form.addClass('working') + form.find('button').addClass('disabled') + }, 200) + + $.request('generate_payment_blob.json', {comment: commentId}, function (token) { + clearTimeout(workingTimer) + form.removeClass('working') + passthroughs.val(token) + form.find('button').removeAttr('disabled').removeClass('disabled') + }) + + return false + }, + + gildComment: function (comment_id, new_title) { + var comment = $('.id-' + comment_id) + + if (!comment.length) { + console.log("couldn't gild comment " + comment_id) + return + } + + var tagline = comment.children('.entry').find('p.tagline'), + icon = tagline.find('.gilded-comment-icon') + + comment.addClass('gilded user-gilded') + if (!icon.length) { + icon = $('') + .addClass('gilded-comment-icon') + tagline.append(icon) + } + icon.attr('title', new_title) + comment.children('.entry').find('.give-gold').parent().remove() + } +}; + +(function($) { + $.gild_comment = function (comment_id, new_title) { + r.gold.gildComment(comment_id, new_title) + $('#gold_form_' + comment_id).fadeOut(400) + } +})(jQuery) diff --git a/r2/r2/templates/comment.html b/r2/r2/templates/comment.html index 2046966af..db8c0d18f 100755 --- a/r2/r2/templates/comment.html +++ b/r2/r2/templates/comment.html @@ -111,6 +111,11 @@ ${parent.collapsed()} ${thing_timestamp(thing, thing.timesince)} ${_("ago")} ${edited(thing, thing.lastedited)} %endif + + % if thing.gilded_message: + + % endif + %if collapse:   diff --git a/r2/r2/templates/goldpayment.html b/r2/r2/templates/goldpayment.html index 7510aea0d..9d10abe24 100644 --- a/r2/r2/templates/goldpayment.html +++ b/r2/r2/templates/goldpayment.html @@ -21,88 +21,90 @@ ############################################################################### <%namespace file="utils.html" import="error_field, success_field, radio_type"/> -<%namespace name="utils" file="utils.html"/> <% from r2.lib.filters import unsafe, safemarkdown from r2.lib.template_helpers import static %> -
-
+
+
+ +
+ ${unsafe(safemarkdown(thing.summary, wrap=False))} - <%utils:round_field title=""> - -

${thing.summary}

- %if thing.giftmessage: -

${_("The following gift note will be attached:")}

+ %if thing.giftmessage: +

${_("The following gift note will be attached:")}

- ${unsafe(safemarkdown(thing.giftmessage))} + ${unsafe(safemarkdown(thing.giftmessage, wrap=False))}
-
+ %endif + + %if thing.pay_from_creddits: +
+ + + + +
+
+ %else: +

${_("Please select a payment method.")}

+ + %if thing.google_id: +
+ + + + + + + + + +
%endif - %if thing.pay_from_creddits: -
- - - - -
- %else: - ${_("Please select a payment method.")} -
- %if thing.google_id: -
- - - - - - - - - -
+
+ + + %if thing.quantity: + %endif + + +
-
- - - %if thing.quantity: - - %endif - - -
- - %if not thing.google_id: -
${_("Note: Google Checkout does not support ongoing subscriptions at this time.")}
- %endif - %endif
- + + %if not thing.google_id: +
${_("Note: Google Checkout does not support ongoing subscriptions at this time.")}
+ %endif + %endif +
+% if not thing.clone_template: +% endif diff --git a/r2/r2/templates/printable.html b/r2/r2/templates/printable.html index 3c79e0945..38fa3d59f 100644 --- a/r2/r2/templates/printable.html +++ b/r2/r2/templates/printable.html @@ -77,10 +77,16 @@ ${self.RenderPrintable()} rowclass = thing.rowstyle + " reported" else: rowclass = thing.rowstyle + if hasattr(thing, "_deleted") and thing._deleted: + rowclass += " deleted" if hasattr(thing, "saved") and thing.saved: rowclass += " saved" if hasattr(thing, "hidden") and thing.hidden: rowclass += " hidden" + if hasattr(thing, "gildings") and thing.gildings > 0: + rowclass += " gilded" + if hasattr(thing, "user_gilded") and thing.user_gilded: + rowclass += " user-gilded" %>
+ + + % endif %if not thing.profilepage and thing.can_reply:
  • ${self.simple_button(_("reply {verb}"), "reply")} diff --git a/r2/r2/templates/thingupdater.html b/r2/r2/templates/thingupdater.html index 43d55cb34..ecdde65bb 100644 --- a/r2/r2/templates/thingupdater.html +++ b/r2/r2/templates/thingupdater.html @@ -35,6 +35,11 @@ }); $.map(friends, show_friend); + var gildings = ${unsafe(simplejson.dumps(thing.gildings))}; + for (var gilded_comment in gildings) { + r.gold.gildComment(gilded_comment, gildings[gilded_comment]); + } + var saves = ${unsafe(simplejson.dumps(list(thing.saves)))}; $.map(saves, show_saved);