mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-29 16:58:21 -05:00
Add reddit gold partners
This commit is contained in:
@@ -214,6 +214,7 @@ def make_map():
|
||||
mc('/gold', controller='forms', action="gold")
|
||||
mc('/gold/creditgild/:passthrough', controller='forms', action='creditgild')
|
||||
mc('/gold/about', controller='front', action='gold_info')
|
||||
mc('/gold/partners', controller='front', action='gold_partners')
|
||||
mc('/gold/thanks', controller='front', action='goldthanks')
|
||||
|
||||
mc('/password', controller='forms', action="password")
|
||||
|
||||
@@ -849,6 +849,17 @@ class ApiController(RedditController, OAuth2ResourceController):
|
||||
notify_user_added("accept_moderator_invite", c.user, c.user, c.site)
|
||||
jquery.refresh()
|
||||
|
||||
@json_validate(VUser(),
|
||||
VGold(),
|
||||
VModhash(),
|
||||
deal=VLength('deal', 100))
|
||||
def POST_claim_gold_partner_deal_code(self, responder, deal):
|
||||
try:
|
||||
return {'code': GoldPartnerDealCode.claim_code(c.user, deal)}
|
||||
except GoldPartnerCodesExhaustedError:
|
||||
return {'error': 'GOLD_PARTNER_CODES_EXHAUSTED',
|
||||
'explanation': _("sorry, we're out of codes!")}
|
||||
|
||||
@validatedForm(VUser('curpass', default=''),
|
||||
VModhash(),
|
||||
password=VPassword(
|
||||
|
||||
@@ -1133,6 +1133,9 @@ class FrontController(RedditController, OAuth2ResourceController):
|
||||
def GET_gold_info(self):
|
||||
return GoldInfoPage(_("gold"), show_sidebar=False).render()
|
||||
|
||||
def GET_gold_partners(self):
|
||||
return GoldPartnersPage(_("gold partners"), show_sidebar=False).render()
|
||||
|
||||
|
||||
class FormsController(RedditController):
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from r2.models import Friends, All, Sub, NotFound, DomainSR, Random, Mod, Random
|
||||
from r2.models import Link, Printable, Trophy, bidding, PromoCampaign, PromotionWeights, Comment
|
||||
from r2.models import Flair, FlairTemplate, FlairTemplateBySubredditIndex
|
||||
from r2.models import USER_FLAIR, LINK_FLAIR
|
||||
from r2.models import GoldPartnerDealCode
|
||||
from r2.models.promo import NO_TRANSACTION, PromotionLog
|
||||
from r2.models.token import OAuth2Client, OAuth2AccessToken
|
||||
from r2.models import traffic
|
||||
@@ -3997,6 +3998,18 @@ class GoldInfoPage(BoringPage):
|
||||
}
|
||||
BoringPage.__init__(self, *args, **kwargs)
|
||||
|
||||
class GoldPartnersPage(BoringPage):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.prices = {
|
||||
"gold_month_price": g.gold_month_price,
|
||||
"gold_year_price": g.gold_year_price,
|
||||
}
|
||||
if c.user_is_loggedin:
|
||||
self.existing_codes = GoldPartnerDealCode.get_codes_for_user(c.user)
|
||||
else:
|
||||
self.existing_codes = []
|
||||
BoringPage.__init__(self, *args, **kwargs)
|
||||
|
||||
class Goldvertisement(Templated):
|
||||
def __init__(self):
|
||||
Templated.__init__(self)
|
||||
|
||||
@@ -153,6 +153,7 @@ string_dict = dict(
|
||||
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](/gold/about)."),
|
||||
gold_summary_comment_page = _("Give *%(recipient)s* a month of [reddit gold](/gold/about) for this comment:"),
|
||||
gold_partners_description = _('reddit gold members get access to exclusive stuff'),
|
||||
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."),
|
||||
|
||||
@@ -23,9 +23,17 @@
|
||||
from r2.lib.db.tdb_sql import make_metadata, index_str, create_table
|
||||
|
||||
from pylons import g, c
|
||||
from pylons.i18n import _
|
||||
from datetime import datetime
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.exc import IntegrityError, OperationalError
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from sqlalchemy.schema import Column
|
||||
from sqlalchemy.sql import and_
|
||||
from sqlalchemy.types import String, Integer
|
||||
|
||||
from xml.dom.minidom import Document
|
||||
from r2.lib.utils import tup, randstr
|
||||
@@ -42,6 +50,9 @@ ENGINE_NAME = 'authorize'
|
||||
ENGINE = g.dbm.get_engine(ENGINE_NAME)
|
||||
METADATA = make_metadata(ENGINE)
|
||||
|
||||
Session = scoped_session(sessionmaker(bind=ENGINE))
|
||||
Base = declarative_base(bind=ENGINE)
|
||||
|
||||
gold_table = sa.Table('reddit_gold', METADATA,
|
||||
sa.Column('trans_id', sa.String, nullable = False,
|
||||
primary_key = True),
|
||||
@@ -66,6 +77,61 @@ indices = [index_str(gold_table, 'status', 'status'),
|
||||
index_str(gold_table, 'subscr_id', 'subscr_id')]
|
||||
create_table(gold_table, indices)
|
||||
|
||||
|
||||
|
||||
class GoldPartnerCodesExhaustedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class GoldPartnerDealCode(Base):
|
||||
"""Promo codes for deals from reddit gold partners."""
|
||||
|
||||
__tablename__ = "reddit_gold_partner_deal_codes"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
deal = Column(String, nullable=False)
|
||||
code = Column(String, nullable=False)
|
||||
user = Column(Integer, nullable=True)
|
||||
|
||||
@classmethod
|
||||
def get_codes_for_user(cls, user):
|
||||
results = Session.query(cls).filter(cls.user == user._id)
|
||||
codes = {r.deal: r.code for r in results}
|
||||
return codes
|
||||
|
||||
@classmethod
|
||||
def claim_code(cls, user, deal):
|
||||
# check if they already have a code for this deal and return it
|
||||
try:
|
||||
result = (Session.query(cls)
|
||||
.filter(and_(cls.user == user._id,
|
||||
cls.deal == deal))
|
||||
.one())
|
||||
return result.code
|
||||
except NoResultFound:
|
||||
pass
|
||||
|
||||
# select an unclaimed code, assign it to the user, and return it
|
||||
try:
|
||||
claiming = (Session.query(cls)
|
||||
.filter(and_(cls.deal == deal,
|
||||
cls.user == None,
|
||||
func.pg_try_advisory_lock(cls.id)))
|
||||
.limit(1)
|
||||
.one())
|
||||
except NoResultFound:
|
||||
raise GoldPartnerCodesExhaustedError
|
||||
|
||||
claiming.user = user._id
|
||||
Session.add(claiming)
|
||||
Session.commit()
|
||||
|
||||
# release the lock
|
||||
Session.query(func.pg_advisory_unlock_all()).all()
|
||||
|
||||
return claiming.code
|
||||
|
||||
|
||||
def create_unclaimed_gold (trans_id, payer_email, paying_id,
|
||||
pennies, days, secret, date,
|
||||
subscr_id = None):
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
transform: @arguments;
|
||||
}
|
||||
|
||||
section#about-gold, section#postcard, .or-box {
|
||||
section#about-gold, section#about-gold-partners, section#postcard, .or-box {
|
||||
font-family: "Bitstream Charter", "Hoefler Text", "Palatino Linotype",
|
||||
"Book Antiqua", Palatino, georgia, garamond, FreeSerif, serif;
|
||||
}
|
||||
|
||||
section#about-gold {
|
||||
section#about-gold, section#about-gold-partners {
|
||||
width: 960px;
|
||||
margin: 40px auto;
|
||||
background: url(../gold/inner-bg.jpg) 11px repeat-y;
|
||||
@@ -92,6 +92,18 @@ section#about-gold {
|
||||
}
|
||||
}
|
||||
|
||||
.claim-code-button {
|
||||
.buy-gold-button;
|
||||
|
||||
cursor: pointer;
|
||||
border-width: 1px;
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
float: right;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
header {
|
||||
// could use box-sizing here, but want to look semi-decent in IE7
|
||||
@width: 960px;
|
||||
@@ -243,6 +255,31 @@ section#about-gold {
|
||||
}
|
||||
}
|
||||
|
||||
section#about-gold-partners {
|
||||
input.code {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
float: right;
|
||||
max-width: 200px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.error {
|
||||
font-size: 16px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
section {
|
||||
.how-to-use p {
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.or-box {
|
||||
background: #817461;
|
||||
margin: 0 auto;
|
||||
|
||||
BIN
r2/r2/public/static/gold/partner-backblaze.png
Normal file
BIN
r2/r2/public/static/gold/partner-backblaze.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -13,6 +13,10 @@ r.gold = {
|
||||
})
|
||||
|
||||
$('.stripe-submit').on('click', this.makeStripeToken)
|
||||
|
||||
$('section#about-gold-partners').on('click', 'input.code', function() {
|
||||
$(this).select()
|
||||
})
|
||||
},
|
||||
|
||||
_toggleCommentGoldForm: function (e) {
|
||||
@@ -183,6 +187,25 @@ r.gold = {
|
||||
)
|
||||
}
|
||||
return false
|
||||
},
|
||||
|
||||
claim_gold_partner_deal_code: function (elem, name) {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: '/api/claim_gold_partner_deal_code.json',
|
||||
data: {'deal': name, 'uh': r.config.modhash},
|
||||
success: function(data) {
|
||||
if ('error' in data) {
|
||||
var $newelem = $('<span class="error">').text(data['explanation'])
|
||||
$(elem).replaceWith($newelem)
|
||||
} else {
|
||||
var $newelem = $('<input type="text" class="code">').attr('value', data['code'])
|
||||
$(elem).replaceWith($newelem)
|
||||
$newelem.select()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
<%!
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.template_helpers import static
|
||||
%>
|
||||
<%namespace file="less.html" import="less_stylesheet"/>
|
||||
@@ -39,22 +40,38 @@
|
||||
<div class="new-mark">${_('New!')}</div>
|
||||
%endif
|
||||
${_md(description_md)}
|
||||
%if caller:
|
||||
${caller.body()}
|
||||
%endif
|
||||
</div>
|
||||
</section>
|
||||
</%def>
|
||||
|
||||
<%def name="goldinfo_header(title, description)">
|
||||
<header>
|
||||
<img class="insignia" src="${static('gold/gold-insignia-big.png')}" alt="">
|
||||
<h1>${title}</h1>
|
||||
<p>${description}</p>
|
||||
<div class="actions">
|
||||
<a class="buy-gold-button" href="/gold">${_('buy reddit gold')}</a>
|
||||
<span class="or">${_('or')}</span>
|
||||
<a class="postcard-button" href="/gold/about#postcard"><img src="${static('gold/stamp.png')}" alt=""> ${_('send us a postcard')}</a>
|
||||
</div>
|
||||
</header>
|
||||
</%def>
|
||||
|
||||
<%def name="goldinfo_footer()">
|
||||
<footer>
|
||||
<h1>${_('become a member today.')}</h1>
|
||||
<p>${_('reddit gold is %(gold_month_price)s / month, or %(gold_year_price)s for a year.') % thing.prices}</p>
|
||||
<a class="buy-gold-button" href="/gold">${_('buy reddit gold')}</a>
|
||||
</footer>
|
||||
</%def>
|
||||
|
||||
<%def name="content()">
|
||||
<section id="about-gold">
|
||||
<header>
|
||||
<img class="insignia" src="${static('gold/gold-insignia-big.png')}" alt="">
|
||||
<h1>${_('Make reddit better.')}</h1>
|
||||
<p>${_('reddit gold adds shiny extra features to your account that are made possible thanks to support from people like you.')}</p>
|
||||
<div class="actions">
|
||||
<a class="buy-gold-button" href="/gold">${_('buy reddit gold')}</a>
|
||||
<span class="or">${_('or')}</span>
|
||||
<a class="postcard-button" href="#postcard"><img src="${static('gold/stamp.png')}" alt=""> ${_('send us a postcard')}</a>
|
||||
</div>
|
||||
</header>
|
||||
${goldinfo_header(_('Make reddit better.'),
|
||||
_('reddit gold adds shiny extra features to your account that are made possible thanks to support from people like you.'))}
|
||||
${feature_item(static('gold/sample-filterall.png'),
|
||||
_(
|
||||
"# Filter specific subreddits from /r/all.\n"
|
||||
@@ -99,15 +116,12 @@
|
||||
<ul>
|
||||
<li><strong>${_('The Lounge.')}</strong> ${_('Access to a super-secret members-only community that may or may not exist.')}</li>
|
||||
<li><strong>${_('Beta test new features.')}</strong> ${_('We occasionally invite gold members to try out new features first.')}</li>
|
||||
<li><strong><a href="/gold/partners">${_('Deals from gold partners.')}</a></strong> ${strings.gold_partners_description}</li>
|
||||
<li><strong>${_('A trophy on your user page.')}</strong> ${_('As thanks for supporting reddit gold.')}</li>
|
||||
<li><strong>${_('More to come.')}</strong> ${_("We have many more ideas for gold features, and will add to this list in the future.")}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<footer>
|
||||
<h1>${_('become a member today.')}</h1>
|
||||
<p>${_('reddit gold is %(gold_month_price)s / month, or %(gold_year_price)s for a year.') % thing.prices}</p>
|
||||
<a class="buy-gold-button" href="/gold">${_('buy reddit gold')}</a>
|
||||
</footer>
|
||||
${goldinfo_footer()}
|
||||
</section>
|
||||
<div class="or-box">${_('or')}</div>
|
||||
<a class="postcard" href="/about/postcards" target="_blank">
|
||||
|
||||
67
r2/r2/templates/goldpartnerspage.html
Normal file
67
r2/r2/templates/goldpartnerspage.html
Normal file
@@ -0,0 +1,67 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be
|
||||
## consistent with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is reddit Inc.
|
||||
##
|
||||
## All portions of the code written by reddit are Copyright (c) 2006-2013
|
||||
## reddit Inc. All Rights Reserved.
|
||||
###############################################################################
|
||||
<%!
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.template_helpers import static
|
||||
%>
|
||||
<%namespace file="goldinfopage.html" import="feature_item, goldinfo_header, goldinfo_footer"/>
|
||||
<%namespace file="less.html" import="less_stylesheet"/>
|
||||
<%namespace file="utils.html" import="_md"/>
|
||||
<%inherit file="reddit.html"/>
|
||||
|
||||
<%def name="stylesheet()">
|
||||
${parent.stylesheet()}
|
||||
${less_stylesheet('goldinfo.less')}
|
||||
</%def>
|
||||
|
||||
<%def name="partner_item(name, how_to_use, img_src, description_md, extra_class='')">
|
||||
<%call expr="feature_item(img_src, description_md, extra_class)">
|
||||
%if c.user.gold:
|
||||
<div class="how-to-use">
|
||||
${_md(how_to_use)}
|
||||
</div>
|
||||
%endif
|
||||
%if name in thing.existing_codes:
|
||||
<input class="code" type="text" value="${thing.existing_codes[name]}">
|
||||
%elif c.user.gold:
|
||||
<a class="claim-code-button" onclick="r.gold.claim_gold_partner_deal_code(this, '${name}')">${_('claim code')}</a>
|
||||
%else:
|
||||
<a class="claim-code-button" href="/gold">${_('buy reddit gold')}</a>
|
||||
%endif
|
||||
</%call>
|
||||
</%def>
|
||||
|
||||
<%def name="content()">
|
||||
<section id="about-gold-partners">
|
||||
${goldinfo_header(_('reddit gold partners'),
|
||||
strings.gold_partners_description)}
|
||||
|
||||
${partner_item('backblaze',
|
||||
_("Claim your code below, then visit https://secure.backblaze.com/gift/XXXXXXX (replacing Xs with your code), and click 'Redeem & Download' to install the product and enter your email/password."),
|
||||
static('gold/partner-backblaze.png'),
|
||||
_('# Backblaze Online Backup\n'
|
||||
'3 months of free service from Backblaze Online Backup.'),
|
||||
'new')}
|
||||
|
||||
${goldinfo_footer()}
|
||||
</section>
|
||||
</%def>
|
||||
Reference in New Issue
Block a user