Skip to content

Commit

Permalink
Features:
Browse files Browse the repository at this point in the history
    * Cassandra
      * Add new cassandra libraries that we'll need to setup.py
      * Select Cassandra seeds at random rather than in order
      * Bugfix in CassandraCache.delete and a faster permacache migration function
      * Like other caches, CassandraCaches need to be able to take (and ignore) a 'time' parameter
      * add Cassandra to the permacache chain
    * beginning of jury duty (later called deputy moderation)

    Additions:
    * Make /r/friends much cheaper at the expense of sorting
    * Add Jury.delete_old(), which removes Account-Trial relations > 3 days old
    * Make the pretty_button() template function's callback optional, so that
      actionless pretty-buttons can be used on the admin details page
    * make .embed listings work for permalink pages (think of this as a first pass to getting blog comments working).  Adds 'limit' and 'depth' parameter to permalink pages
    * Added final redditheader.html pretty-button class
    * new iframe ads; also make button.js static
    * Usage sampling

    Bugfixes:
    * Stop adding batched time query recalculations to the queue at all except through the catch_up_batch_queries function
    * Superflous comma might be causing IE7 to barf
    * Change the byurl keys again, to fit in memcaches 251 character limit
    * Indentation error causing non-sponsors to be able to get to the advert listing
    * Move to a custom build of pylibmc that doesn't hold the GIL during blocking operations
    * Convert some cache.gets to cache.get_multis, and implement our own thread-safety around pylibmc's client
    * Make search caching a little smarter for time searches
    * Make the ads not be cached for 30 seconds each, ie. more random
    * fix deleted things on profile pages
  • Loading branch information
ketralnis authored and KeyserSosa committed May 17, 2010
1 parent a402d48 commit 67814d5
Show file tree
Hide file tree
Showing 80 changed files with 2,613 additions and 401 deletions.
12 changes: 10 additions & 2 deletions r2/example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ log_path =

locale = C
memcaches = 127.0.0.1:11211
permacaches = 127.0.0.1:11211
rendercaches = 127.0.0.1:11211
rec_cache = 127.0.0.1:11311

# -- permacache options --
# permacache is memcaches -> cassanda -> memcachedb
# memcaches that sit in front of cassandra
permacache_memcaches = 127.0.0.1:11211
# cassandra hosts. one of these will be chosen at random by pycassa
cassandra_seeds = 127.0.0.1:9160
# memcachedbs
permacaches = 127.0.0.1:11211

# site tracking urls. All urls are assumed to be to an image unless
# otherwise noted:
tracker_url =
Expand Down Expand Up @@ -109,7 +117,7 @@ timezone = UTC
lang = en
monitored_servers = localhost

enable_usage_stats = false
usage_sampling = 0.0

#query cache settings
num_query_queue_workers = 0
Expand Down
10 changes: 7 additions & 3 deletions r2/r2/config/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def make_map(global_conf={}, app_conf={}):
mc('/prefs/:location', controller='front',
action='prefs', location='options')

mc('/juryduty', controller='front', action='juryduty')

mc('/info/0:article/*rest', controller = 'front',
action='oldinfo', dest='comments', type='ancient')
mc('/info/:article/:dest/:comment', controller='front',
Expand Down Expand Up @@ -206,6 +208,8 @@ def make_map(global_conf={}, app_conf={}):
requirements=dict(action="promote|unpromote|new_promo|link_thumb|freebie|promote_note|update_pay|refund|traffic_viewer|rm_traffic_viewer"))
mc('/api/:action', controller='api')

mc("/button_info", controller="api", action="info", limit = 1)

mc('/captcha/:iden', controller='captcha', action='captchaimg')

mc('/mediaembed/:link', controller="mediaembed", action="mediaembed")
Expand All @@ -224,9 +228,9 @@ def make_map(global_conf={}, app_conf={}):
mc('/authorize_embed', controller = 'front', action = 'authorize_embed')

# Used for showing ads
mc("/ads/", controller = "mediaembed", action = "ad")
mc("/ads/r/:reddit_name", controller = "mediaembed", action = "ad")
mc("/ads/:codename", controller = "mediaembed", action = "ad_by_codename")
mc("/ads/", controller = "ad", action = "ad")
mc("/ads/r/:reddit_name", controller = "ad", action = "ad")
mc("/ads/:codename", controller = "ad", action = "ad_by_codename")

mc('/comscore-iframe/', controller='mediaembed', action='comscore')
mc('/comscore-iframe/*url', controller='mediaembed', action='comscore')
Expand Down
1 change: 1 addition & 0 deletions r2/r2/controllers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from errorlog import ErrorlogController
from promotecontroller import PromoteController
from mediaembed import MediaembedController
from mediaembed import AdController

from querycontroller import QueryController

Expand Down
6 changes: 3 additions & 3 deletions r2/r2/controllers/ads.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@

class AdsController(RedditController):

@validate(VSponsor())
@validate(VAdmin())
def GET_index(self):
res = AdminPage(content = AdminAds(),
show_sidebar = False,
title = 'ads').render()
return res

@validate(VSponsor(),
@validate(VAdmin(),
ad = VAdByCodename('adcn'))
def GET_assign(self, ad):
if ad is None:
Expand All @@ -44,7 +44,7 @@ def GET_assign(self, ad):
title='assign an ad to a community').render()
return res

@validate(VSponsor(),
@validate(VAdmin(),
ad = VAdByCodename('adcn'))
def GET_srs(self, ad):
if ad is None:
Expand Down
107 changes: 79 additions & 28 deletions r2/r2/controllers/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,12 @@
from r2.models.subreddit import Default as DefaultSR

from r2.lib.utils import get_title, sanitize_url, timeuntil, set_last_modified
from r2.lib.utils import query_string, link_from_url, timefromnow
from r2.lib.utils import timeago, tup
from r2.lib.utils import query_string, timefromnow
from r2.lib.utils import timeago, tup, filter_links
from r2.lib.pages import FriendList, ContributorList, ModList, \
BannedList, BoringPage, FormPage, CssError, UploadedImage, \
ClickGadget
from r2.lib.utils.trial_utils import indict, on_trial
from r2.lib.pages.things import wrap_links, default_thing_wrapper

from r2.lib import spreadshirt
Expand Down Expand Up @@ -78,16 +79,19 @@ class ApiController(RedditController):
def ajax_login_redirect(self, form, jquery, dest):
form.redirect("/login" + query_string(dict(dest=dest)))

@validate(link = VUrl(['url']),
@validate(link1 = VUrl(['url']),
link2 = VByName('id'),
count = VLimit('limit'))
def GET_info(self, link, count):
def GET_info(self, link1, link2, count):
"""
Gets a listing of links which have the provided url.
"""
if not link or 'url' not in request.params:
return abort(404, 'not found')
links = []
if link2:
links = filter_links(tup(link2), filter_spam = False)
elif link1 and ('ALREADY_SUB', 'url') in c.errors:
links = filter_links(tup(link1), filter_spam = False)

links = link_from_url(request.params.get('url'), filter_spam = False)
if not links:
return abort(404, 'not found')

Expand Down Expand Up @@ -577,6 +581,15 @@ def POST_report(self, thing):
return
Report.new(c.user, thing)

@noresponse(VAdmin(), VModhash(),
thing = VByName('id'))
def POST_indict(self, thing):
'''put something on trial'''
if not thing:
log_text("indict: no thing", level="warning")

indict(thing)

@validatedForm(VUser(),
VModhash(),
item = VByNameIfAuthor('thing_id'),
Expand Down Expand Up @@ -751,6 +764,42 @@ def POST_share(self, shareform, jquery, emails, thing, share_from, reply_to,
VRatelimit.ratelimit(rate_user=True, rate_ip = True,
prefix = "rate_share_")

@noresponse(VUser(),
VModhash(),
ip = ValidIP(),
dir = VInt('dir', min=-1, max=1),
thing = VByName('id'))
def POST_juryvote(self, dir, thing, ip):
if not thing:
log_text("juryvote: no thing", level="warning")
return

if not ip:
log_text("juryvote: no ip", level="warning")
return

if dir is None:
log_text("juryvote: no dir", level="warning")
return

j = Jury.by_account_and_defendant(c.user, thing)

if not on_trial([thing]).get(thing._fullname,False):
log_text("juryvote: not on trial", level="warning")
return

if not j:
log_text("juryvote: not on the jury", level="warning")
return

log_text("juryvote",
"%s cast a %d juryvote on %r" % (c.user.name, dir, thing),
level="info")

j._name = str(dir)
j._date = c.start_time
j._commit()

@noresponse(VUser(),
VModhash(),
vote_type = VVotehash(('vh', 'id')),
Expand All @@ -774,24 +823,21 @@ def POST_vote(self, dir, thing, ip, vote_type):
g.log.debug("POST_vote: ignoring old vote on %s" % thing._fullname)
store = False

# in a lock to prevent duplicate votes from people
# double-clicking the arrows
with g.make_lock('vote_lock(%s,%s)' % (c.user._id36, thing._id36)):
dir = (True if dir > 0
else False if dir < 0
else None)

organic = vote_type == 'organic'
queries.queue_vote(user, thing, dir, ip, organic, store = store,
cheater = (errors.CHEATER, None) in c.errors)
if store:
#update relevant caches
if isinstance(thing, Link):
set_last_modified(c.user, 'liked')
set_last_modified(c.user, 'disliked')

# flag search indexer that something has changed
changed(thing)
dir = (True if dir > 0
else False if dir < 0
else None)

organic = vote_type == 'organic'
queries.queue_vote(user, thing, dir, ip, organic, store = store,
cheater = (errors.CHEATER, None) in c.errors)
if store:
# update relevant caches
if isinstance(thing, Link):
set_last_modified(c.user, 'liked')
set_last_modified(c.user, 'disliked')

# flag search indexer that something has changed
changed(thing)

@validatedForm(VUser(),
VModhash(),
Expand Down Expand Up @@ -1444,9 +1490,10 @@ def POST_edit_error(self, form, jquery, hexkey, nickname, status):
colliding_ad=VAdByCodename(("codename", "fullname")),
codename = VLength("codename", max_length = 100),
imgurl = VLength("imgurl", max_length = 1000),
raw_html = VLength("raw_html", max_length = 10000),
linkurl = VLength("linkurl", max_length = 1000))
def POST_editad(self, form, jquery, ad, colliding_ad, codename,
imgurl, linkurl):
imgurl, raw_html, linkurl):
if form.has_errors(("codename", "imgurl", "linkurl"),
errors.NO_TEXT):
pass
Expand All @@ -1459,12 +1506,16 @@ def POST_editad(self, form, jquery, ad, colliding_ad, codename,
return

if ad is None:
Ad._new(codename, imgurl, linkurl)
Ad._new(codename,
imgurl=imgurl,
raw_html=raw_html,
linkurl=linkurl)
form.set_html(".status", "saved. reload to see it.")
return

ad.codename = codename
ad.imgurl = imgurl
ad.raw_html = raw_html
ad.linkurl = linkurl
ad._commit()
form.set_html(".status", _('saved'))
Expand Down Expand Up @@ -1614,7 +1665,7 @@ def POST_removetrophy(self, form, jquery, trophy):
@validatedForm(links = VByName('links', thing_cls = Link, multiple = True),
show = VByName('show', thing_cls = Link, multiple = False))
def POST_fetch_links(self, form, jquery, links, show):
l = wrap_links(links, listing_cls = OrganicListing,
l = wrap_links(links, listing_cls = SpotlightListing,
num_margin = 0, mid_margin = 0)
jquery(".content").replace_things(l, stubs = True)

Expand Down
2 changes: 2 additions & 0 deletions r2/r2/controllers/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,5 @@ def GET_bookmarklets(self):
return BoringPage(_("bookmarklets"),
show_sidebar = False,
content=Bookmarklets()).render()


70 changes: 66 additions & 4 deletions r2/r2/controllers/front.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,11 @@ def GET_shirt(self, article):
comment = VCommentID('comment'),
context = VInt('context', min = 0, max = 8),
sort = VMenu('controller', CommentSortMenu),
num_comments = VMenu('controller', NumCommentsMenu))
def GET_comments(self, article, comment, context, sort, num_comments):
num_comments = VMenu('controller', NumCommentsMenu),
limit = VInt('limit'),
depth = VInt('depth'))
def GET_comments(self, article, comment, context, sort, num_comments,
limit, depth):
"""Comment page for a given 'article'."""
if comment and comment.link_id != article._id:
return self.abort404()
Expand Down Expand Up @@ -228,8 +231,18 @@ def GET_comments(self, article, comment, context, sort, num_comments):
user_num = c.user.pref_num_comments or g.num_comments
num = g.max_comments if num_comments == 'true' else user_num

kw = {}
# allow depth to be reset (I suspect I'll turn the VInt into a
# validator on my next pass of .compact)
if depth is not None and 0 < depth < MAX_RECURSION:
kw['max_depth'] = depth
# allow the user's total count preferences to be overwritten
# (think of .embed as the use case together with depth=1)x
if limit is not None and 0 < limit < g.max_comments:
num = limit

builder = CommentBuilder(article, CommentSortMenu.operator(sort),
comment, context)
comment, context, **kw)
listing = NestedListing(builder, num = num,
parent_name = article._fullname)

Expand Down Expand Up @@ -262,6 +275,55 @@ def GET_comments(self, article, comment, context, sort, num_comments):
infotext = infotext).render()
return res

@validate(VUser())
def GET_juryduty(self):
displayPane = PaneStack()

active_trials = {}
finished_trials = {}

juries = Jury.by_account(c.user)

trials = on_trial([j._thing2 for j in juries])

for j in juries:
defendant = j._thing2

if trials.get(defendant._fullname, False):
active_trials[defendant._fullname] = j._name
else:
finished_trials[defendant._fullname] = j._name

if active_trials:
fullnames = sorted(active_trials.keys(), reverse=True)

def my_wrap(thing):
w = Wrapped(thing)
w.hide_score = True
w.likes = None
w.trial_mode = True
w.render_class = LinkOnTrial
w.juryvote = active_trials[thing._fullname]
return w

listing = wrap_links(fullnames, wrapper=my_wrap)
displayPane.append(InfoBar(strings.active_trials,
extra_class="mellow"))
displayPane.append(listing)

if finished_trials:
fullnames = sorted(finished_trials.keys(), reverse=True)
listing = wrap_links(fullnames)
displayPane.append(InfoBar(strings.finished_trials,
extra_class="mellow"))
displayPane.append(listing)

displayPane.append(InfoBar(strings.more_info_link %
dict(link="/help/juryduty"),
extra_class="mellow"))

return Reddit(content = displayPane).render()

@validate(VUser(),
location = nop("location"))
def GET_prefs(self, location=''):
Expand Down Expand Up @@ -320,7 +382,7 @@ def GET_editreddit(self, location, num, after, reverse, count, created):
if is_moderator and location == 'edit':
pane = PaneStack()
if created == 'true':
pane.append(InfoBar(message = _('your reddit has been created')))
pane.append(InfoBar(message = strings.sr_created))
pane.append(CreateSubreddit(site = c.site))
elif location == 'moderators':
pane = ModList(editable = is_moderator)
Expand Down
Loading

0 comments on commit 67814d5

Please sign in to comment.