mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-25 23:08:22 -05:00
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.
This commit is contained in:
committed by
Max Goodman
parent
1f72971e5d
commit
b3bb35131e
@@ -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,
|
||||
))
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -286,6 +286,7 @@ module["reddit"] = LocalizedModule("reddit.js",
|
||||
"wiki.js",
|
||||
"reddit.js",
|
||||
"apps.js",
|
||||
"gold.js",
|
||||
)
|
||||
|
||||
module["mobile"] = LocalizedModule("mobile.js",
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."),
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -16,4 +16,5 @@ $(function() {
|
||||
r.interestbar.init()
|
||||
r.apps.init()
|
||||
r.wiki.init()
|
||||
r.gold.init()
|
||||
})
|
||||
|
||||
92
r2/r2/public/static/js/gold.js
Normal file
92
r2/r2/public/static/js/gold.js
Normal file
@@ -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 = $('<span>')
|
||||
.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)
|
||||
@@ -111,6 +111,11 @@ ${parent.collapsed()}
|
||||
${thing_timestamp(thing, thing.timesince)} ${_("ago")}
|
||||
${edited(thing, thing.lastedited)}
|
||||
%endif
|
||||
|
||||
% if thing.gilded_message:
|
||||
<span class="gilded-comment-icon" title="${thing.gilded_message}"></span>
|
||||
% endif
|
||||
|
||||
%if collapse:
|
||||
<a href="#" class="expand"
|
||||
onclick="return showcomment(this)">
|
||||
|
||||
@@ -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
|
||||
%>
|
||||
|
||||
<div class="content gold-form">
|
||||
<div class="spacer">
|
||||
<div class="gold-form gold-payment ${'cloneable' if thing.clone_template else ''}">
|
||||
<div class="roundfield">
|
||||
<img src="${static('reddit_gold-70.png')}" class="gold-logo">
|
||||
<div class="roundfield-content">
|
||||
${unsafe(safemarkdown(thing.summary, wrap=False))}
|
||||
|
||||
<%utils:round_field title="">
|
||||
<img src="${static('reddit_gold-70.png')}" class="gold-logo"/>
|
||||
<h1>${thing.summary}</h1>
|
||||
%if thing.giftmessage:
|
||||
<h1>${_("The following gift note will be attached:")}</h1>
|
||||
%if thing.giftmessage:
|
||||
<p>${_("The following gift note will be attached:")}</p>
|
||||
<div class="giftmessage">
|
||||
${unsafe(safemarkdown(thing.giftmessage))}
|
||||
${unsafe(safemarkdown(thing.giftmessage, wrap=False))}
|
||||
</div>
|
||||
<br/>
|
||||
%endif
|
||||
|
||||
%if thing.pay_from_creddits:
|
||||
<form id="giftgold" action="/api/spendcreddits" method="post"
|
||||
class="content"
|
||||
onsubmit="return post_form(this, 'spendcreddits');">
|
||||
<input type="hidden" name="months" value="${thing.months}">
|
||||
<input type="hidden" name="passthrough" value="${thing.passthrough}" class="passthrough">
|
||||
<button class="btn gold-button">${_("give")}</button>
|
||||
<span class="status"></span>
|
||||
<div class="throbber"></div>
|
||||
</form>
|
||||
%else:
|
||||
<p>${_("Please select a payment method.")}</p>
|
||||
|
||||
%if thing.google_id:
|
||||
<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/${thing.google_id}"
|
||||
id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm"
|
||||
class="gold-checkout google-checkout"
|
||||
data-vendor="google-checkout"
|
||||
target="${'_blank' if thing.clone_template else '_top'}">
|
||||
<input name="item_name_1" type="hidden" value="creddits">
|
||||
<input name="item_description_1" type="hidden"
|
||||
%if thing.months != thing.quantity:
|
||||
value="${thing.quantity} year(s) of reddit gold"
|
||||
%else:
|
||||
value="${thing.quantity} month(s) of reddit gold"
|
||||
%endif
|
||||
>
|
||||
<input name="item_quantity_1" type="hidden"
|
||||
value="${thing.quantity}">
|
||||
<input name="item_price_1" type="hidden"
|
||||
value="${thing.price}">
|
||||
<input type="hidden"
|
||||
name="shopping-cart.items.item-1.merchant-private-item-data"
|
||||
value="${thing.passthrough}" class="passthrough">
|
||||
<input name="item_currency_1" type="hidden" value="USD">
|
||||
<input name="_charset_" type="hidden" value="utf-8">
|
||||
<input type="hidden" name="analyticsdata" value="">
|
||||
<button class="btn gold-button">${_("Google Checkout")}</button>
|
||||
</form>
|
||||
%endif
|
||||
|
||||
%if thing.pay_from_creddits:
|
||||
<form id="giftgold" action="/api/spendcreddits" method="post"
|
||||
class="content"
|
||||
onsubmit="return post_form(this, 'spendcreddits');">
|
||||
<input type="hidden" name="months" value="${thing.months}" />
|
||||
<input type="hidden" name="passthrough" value="${thing.passthrough}" />
|
||||
<button class="btn gold-button">${_("give")}</button>
|
||||
<span class="status"></span>
|
||||
</form>
|
||||
%else:
|
||||
${_("Please select a payment method.")}
|
||||
<br/>
|
||||
%if thing.google_id:
|
||||
<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/${thing.google_id}"
|
||||
id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm"
|
||||
class="gold-checkout google-checkout"
|
||||
data-vendor="google-checkout"
|
||||
target="_top" style="display:inline">
|
||||
<input name="item_name_1" type="hidden" value="creddits"/>
|
||||
<input name="item_description_1" type="hidden"
|
||||
%if thing.months != thing.quantity:
|
||||
value="${thing.quantity} year(s) of reddit gold"
|
||||
%else:
|
||||
value="${thing.quantity} month(s) of reddit gold"
|
||||
%endif
|
||||
/>
|
||||
<input name="item_quantity_1" type="hidden"
|
||||
value="${thing.quantity}"/>
|
||||
<input name="item_price_1" type="hidden"
|
||||
value="${thing.price}"/>
|
||||
<input type="hidden"
|
||||
name="shopping-cart.items.item-1.merchant-private-item-data"
|
||||
value="${thing.passthrough}"/>
|
||||
<input name="item_currency_1" type="hidden" value="USD"/>
|
||||
<input name="_charset_" type="hidden" value="utf-8"/>
|
||||
<input type="hidden" name="analyticsdata" value="">
|
||||
<button class="btn gold-button">${_("Google Checkout")}</button>
|
||||
</form>
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"
|
||||
class="gold-checkout"
|
||||
data-vendor="paypal" target="${'_blank' if thing.clone_template else '_top'}">
|
||||
<input type="hidden" name="cmd" value="_s-xclick">
|
||||
<input type="hidden" name="custom" value="${thing.passthrough}" class="passthrough">
|
||||
%if thing.quantity:
|
||||
<input type="hidden" name="quantity" value="${thing.quantity}">
|
||||
%endif
|
||||
<input type="hidden" name="hosted_button_id" value="${thing.paypal_buttonid}">
|
||||
<button type="submit" class="btn gold-button">${_("PayPal")}</button>
|
||||
</form>
|
||||
|
||||
<form action="https://www.paypal.com/cgi-bin/webscr" method="post"
|
||||
style="display:inline" class="gold-checkout"
|
||||
data-vendor="paypal">
|
||||
<input type="hidden" name="cmd" value="_s-xclick" />
|
||||
<input type="hidden" name="custom" value="${thing.passthrough}" />
|
||||
%if thing.quantity:
|
||||
<input type="hidden" name="quantity" value="${thing.quantity}" />
|
||||
%endif
|
||||
<input type="hidden" name="hosted_button_id" value="${thing.paypal_buttonid}" />
|
||||
<button type="submit" class="btn gold-button">${_("PayPal")}</button>
|
||||
</form>
|
||||
|
||||
%if not thing.google_id:
|
||||
<div class="note">${_("Note: Google Checkout does not support ongoing subscriptions at this time.")}</div>
|
||||
%endif
|
||||
%endif
|
||||
<div class="throbber"></div>
|
||||
</%utils:round_field>
|
||||
|
||||
%if not thing.google_id:
|
||||
<div class="note">${_("Note: Google Checkout does not support ongoing subscriptions at this time.")}</div>
|
||||
%endif
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
% if not thing.clone_template:
|
||||
<script src="//checkout.google.com/files/digital/ga_post.js"
|
||||
type="text/javascript"></script>
|
||||
% endif
|
||||
|
||||
|
||||
@@ -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"
|
||||
%>
|
||||
<div class="${self.thing_css_class(thing)} ${rowclass} ${unsafe(cls)} ${selflink}"
|
||||
${thing.display} onclick="click_thing(this)"
|
||||
|
||||
@@ -295,6 +295,14 @@
|
||||
%endif
|
||||
${self.banbuttons()}
|
||||
${self.distinguish()}
|
||||
% if thing.can_gild:
|
||||
<li>
|
||||
<a href="/gold?goldtype=gift&months=1&comment=${thing.thing._fullname}"
|
||||
title="${_("give reddit gold in appreciation of this comment.")}"
|
||||
class="give-gold login-required"
|
||||
>${_("give gold")}</a>
|
||||
</li>
|
||||
% endif
|
||||
%if not thing.profilepage and thing.can_reply:
|
||||
<li>
|
||||
${self.simple_button(_("reply {verb}"), "reply")}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user