Skip to content

Commit 68a06c5

Browse files
committed
April 2011 Merge
* Show "this post is archived" message for things we won't allow votes on. * Don't mark messages read when pulled via RSS * Make compact (smartphone) interface respect user's toolbar * preferences. * Make mobile interface more friendly for kindles. * Fix bug that caused comment tree corruption. * Use cachebuster on traffic pixel for more accurate tracking. * Make apps restart themselves after a configurable number of requests. * Move to pycassa 1.0.8. * Fix bug in calculations for "best" sort. * Fixes for Firefox Mobile * Add a global flag to disable editing of the wiki. * Move the child-comment collapse button to the left. * Updated list of disallows in robots.txt to save needless hits from crawlers. * Fix vote_q by splitting it into vote_link_q and vote_comment_q. * Fix bug where /reddits crashes due to 'promos' subreddit.
1 parent 7fff900 commit 68a06c5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1482
-505
lines changed

config/cassandra/cassandra.yaml

+3-22
Original file line numberDiff line numberDiff line change
@@ -92,26 +92,7 @@ commitlog_sync_period_in_ms: 10000
9292
# the topology of the ring. You must change this if you are running
9393
# multiple nodes!
9494
seeds:
95-
- pmc01
96-
- pmc02
97-
- pmc03
98-
- pmc04
99-
- pmc05
100-
- pmc06
101-
- pmc07
102-
- pmc08
103-
- pmc09
104-
- pmc10
105-
- pmc11
106-
- pmc12
107-
- pmc13
108-
- pmc14
109-
- pmc15
110-
- pmc16
111-
- pmc17
112-
- pmc18
113-
- pmc19
114-
- pmc20
95+
- 127.0.0.1
11596

11697
# Access mode. mmapped i/o is substantially faster, but only practical on
11798
# a 64bit machine (which notably does not include EC2 "small" instances)
@@ -155,7 +136,7 @@ storage_port: 7000
155136
# address associated with the hostname (it might not be).
156137
#
157138
# Setting this to 0.0.0.0 is always wrong.
158-
listen_address:
139+
listen_address: 127.0.0.1
159140

160141
# The address to bind the Thrift RPC service to -- clients connect
161142
# here. Unlike ListenAddress above, you *can* specify 0.0.0.0 here if
@@ -425,7 +406,7 @@ index_interval: 128
425406
keyspaces:
426407
- name: reddit
427408
replica_placement_strategy: org.apache.cassandra.locator.RackUnawareStrategy
428-
replication_factor: 3
409+
replication_factor: 1
429410
column_families:
430411
- column_type: Standard
431412
compare_with: BytesType

r2/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js ui.core.js ui.datepicker.js sponsored.js jquery.flot.js compact.js
2525
# CSS targets
2626
main_css = reddit.css
27-
css_targets = reddit-ie6-hax.css reddit-ie7-hax.css mobile.css spreadshirt.css compact.css
27+
css_targets = reddit-ie6-hax.css reddit-ie7-hax.css mobile.css spreadshirt.css compact.css
2828

2929
SED=sed
3030
CAT=cat

r2/example.ini

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ log_start = true
2323
amqp_logging = false
2424
# emergency measures: makes the site read only
2525
read_only_mode = false
26+
# global switch for wiki write permissions
27+
allow_wiki_editing = true
2628

2729
# -- SECRETS! <-- update these first! --
2830
# global secret
@@ -37,6 +39,8 @@ INDEXTANK_API_URL =
3739
# -- important settings --
3840
# the domain that this app serves itself up as
3941
domain = reddit.local
42+
# the short domain (like redd.it)
43+
shortdomain =
4044
# if you use www for the old-timey feel, put it here
4145
domain_prefix =
4246
# the user used for "system" operations and messages
@@ -310,6 +314,8 @@ MIN_RATE_LIMIT_COMMENT_KARMA = 1
310314
QUOTA_THRESHOLD = 5
311315
# Links and comments older than this many days qualify for historic preservation
312316
REPLY_AGE_LIMIT = 180
317+
# Links and comments older than this many days can't be voted on
318+
VOTE_AGE_LIMIT = 180
313319

314320
# min amount of karma to edit
315321
WIKI_KARMA = 100
@@ -341,6 +347,11 @@ sr_dropdown_threshold = 15
341347
# seconds of each other
342348
comment_visits_period = 600
343349

350+
# Set this to a nonzero range and the server will restart after this many
351+
# minutes have passed
352+
LOGANS_RUN_LOW = 0
353+
LOGANS_RUN_HIGH = 0
354+
344355
#user-agents to rate-limit
345356
agents =
346357

r2/r2/controllers/api.py

+9-7
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,6 @@ def POST_submit(self, form, jquery, url, selftext, kind, title,
313313
form.set_html(".status", md)
314314
return
315315

316-
317316
# well, nothing left to do but submit it
318317
l = Link._submit(request.post.title, url if kind == 'link' else 'self',
319318
c.user, sr, ip, spam=c.user._spam)
@@ -819,6 +818,7 @@ def POST_comment(self, commentform, jquery, parent, comment, ip):
819818
parent_age = c.start_time - parent._date
820819
if parent_age.days > g.REPLY_AGE_LIMIT:
821820
c.errors.add(errors.TOO_OLD, field = "parent")
821+
822822
#remove the ratelimit error if the user's karma is high
823823
if not should_ratelimit:
824824
c.errors.remove((errors.RATELIMIT, 'ratelimit'))
@@ -975,8 +975,6 @@ def POST_juryvote(self, dir, thing, ip):
975975
dir = VInt('dir', min=-1, max=1),
976976
thing = VByName('id'))
977977
def POST_vote(self, dir, thing, ip, vote_type):
978-
from r2.models.admintools import valid_vote
979-
980978
ip = request.ip
981979
user = c.user
982980
store = True
@@ -988,7 +986,9 @@ def POST_vote(self, dir, thing, ip, vote_type):
988986
reject_vote(thing)
989987
store = False
990988

991-
if not valid_vote(thing):
989+
thing_age = c.start_time - thing._date
990+
if thing_age.days > g.VOTE_AGE_LIMIT:
991+
g.log.debug("ignoring vote on old thing %s" % thing._fullname)
992992
store = False
993993

994994
if getattr(c.user, "suspicious", False):
@@ -1626,9 +1626,11 @@ def POST_password(self, form, jquery, user):
16261626
elif form.has_errors('name', errors.NO_EMAIL_FOR_USER):
16271627
return
16281628
else:
1629-
emailer.password_email(user)
1630-
form.set_html(".status",
1631-
_("an email will be sent to that account's address shortly"))
1629+
if emailer.password_email(user):
1630+
form.set_html(".status",
1631+
_("an email will be sent to that account's address shortly"))
1632+
else:
1633+
form.set_html(".status", _("try again tomorrow"))
16321634

16331635

16341636
@validatedForm(cache_evt = VCacheKey('reset', ('key',)),

r2/r2/controllers/front.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -240,13 +240,15 @@ def GET_comments(self, article, comment, context, sort, limit, depth):
240240
if num > g.max_comments_gold:
241241
displayPane.append(InfoBar(message =
242242
strings.over_comment_limit_gold
243-
% g.max_comments_gold))
243+
% max(0, g.max_comments_gold)))
244244
num = g.max_comments_gold
245245
elif num > g.max_comments:
246-
displayPane.append(InfoBar(message =
246+
if limit:
247+
displayPane.append(InfoBar(message =
247248
strings.over_comment_limit
248-
% dict(max=g.max_comments,
249-
goldmax=g.max_comments_gold)))
249+
% dict(max=max(0, g.max_comments),
250+
goldmax=max(0,
251+
g.max_comments_gold))))
250252
num = g.max_comments
251253

252254
# if permalink page, add that message first to the content
@@ -960,7 +962,7 @@ def GET_validuser(self):
960962
returns their user name"""
961963
c.response_content_type = 'text/plain'
962964
if c.user_is_loggedin:
963-
perm = str(c.user.can_wiki())
965+
perm = str(g.allow_wiki_editing and c.user.can_wiki())
964966
c.response.content = c.user.name + "," + perm
965967
else:
966968
c.response.content = ''
@@ -1064,3 +1066,4 @@ def GET_gold(self, goldtype, period, months,
10641066
giftmessage, passthrough)
10651067
).render()
10661068

1069+

r2/r2/controllers/listingcontroller.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -723,7 +723,7 @@ def query(self):
723723

724724
@validate(VUser(),
725725
message = VMessageID('mid'),
726-
mark = VOneOf('mark',('true','false'), default = 'true'))
726+
mark = VOneOf('mark',('true','false')))
727727
def GET_listing(self, where, mark, message, subwhere = None, **env):
728728
if not (c.default_sr or c.site.is_moderator(c.user) or c.user_is_admin):
729729
abort(403, "forbidden")
@@ -732,7 +732,14 @@ def GET_listing(self, where, mark, message, subwhere = None, **env):
732732
else:
733733
self.where = where
734734
self.subwhere = subwhere
735-
self.mark = mark
735+
if mark is not None:
736+
self.mark = mark
737+
elif is_api():
738+
self.mark = 'false'
739+
elif c.render_style and c.render_style == "xml":
740+
self.mark = 'false'
741+
else:
742+
self.mark = 'true'
736743
self.message = message
737744
return ListingController.GET_listing(self, **env)
738745

@@ -777,6 +784,11 @@ def query(self):
777784
# Consider resurrecting when it is not the World Cup
778785
#if c.content_langs != 'all':
779786
# reddits._filter(Subreddit.c.lang == c.content_langs)
787+
788+
if g.domain != 'reddit.com':
789+
# don't try to render special subreddits (like promos)
790+
reddits._filter(Subreddit.c.author_id != -1)
791+
780792
if not c.over18:
781793
reddits._filter(Subreddit.c.over_18 == False)
782794

r2/r2/controllers/reddit_base.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,11 @@ def post(self):
594594
domain = v.domain,
595595
expires = v.expires)
596596

597+
if g.logans_run_limit:
598+
if c.start_time > g.logans_run_limit and not g.shutdown:
599+
g.log.info("Time to restart. It's been an honor serving with you.")
600+
g.shutdown = 'init'
601+
597602
if g.usage_sampling <= 0.0:
598603
return
599604

@@ -720,7 +725,7 @@ def pre(self):
720725
c.show_mod_mail = Subreddit.reverse_moderator_ids(c.user)
721726
c.user_is_admin = maybe_admin and c.user.name in g.admins
722727
c.user_is_sponsor = c.user_is_admin or c.user.name in g.sponsors
723-
if not g.disallow_db_writes:
728+
if request.path != '/validuser' and not g.disallow_db_writes:
724729
c.user.update_last_visit(c.start_time)
725730

726731
c.over18 = over18()
@@ -819,3 +824,4 @@ def search_fail(self, exception):
819824
request.environ['retry_after'] = 60
820825

821826
abort(503)
827+

r2/r2/controllers/validator/validator.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -520,16 +520,17 @@ class VByName(Validator):
520520
splitter = re.compile('[ ,]+')
521521
def __init__(self, param, thing_cls = None, multiple = False,
522522
error = errors.NO_THING_ID, **kw):
523-
self.re = fullname_regex(thing_cls, multiple)
523+
self.re = fullname_regex(thing_cls)
524524
self.multiple = multiple
525525
self._error = error
526526

527527
Validator.__init__(self, param, **kw)
528528

529529
def run(self, items):
530-
if items and self.re.match(items):
531-
if self.multiple:
532-
items = filter(None, self.splitter.split(items))
530+
if items and self.multiple:
531+
items = [item for item in self.splitter.split(items)
532+
if item and self.re.match(item)]
533+
if items and (self.multiple or self.re.match(items)):
533534
try:
534535
return Thing._by_fullname(items, return_dict = False,
535536
data=True)

r2/r2/lib/app_globals.py

+17
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,15 @@ class Globals(object):
4747
'MIN_DOWN_KARMA',
4848
'MIN_RATE_LIMIT_KARMA',
4949
'MIN_RATE_LIMIT_COMMENT_KARMA',
50+
'VOTE_AGE_LIMIT',
5051
'REPLY_AGE_LIMIT',
5152
'WIKI_KARMA',
5253
'HOT_PAGE_AGE',
5354
'MODWINDOW',
5455
'RATELIMIT',
5556
'QUOTA_THRESHOLD',
57+
'LOGANS_RUN_LOW',
58+
'LOGANS_RUN_HIGH',
5659
'num_comments',
5760
'max_comments',
5861
'max_comments_gold',
@@ -84,6 +87,7 @@ class Globals(object):
8487
'amqp_logging',
8588
'read_only_mode',
8689
'frontpage_dart',
90+
'allow_wiki_editing',
8791
]
8892

8993
tuple_props = ['stalecaches',
@@ -177,6 +181,7 @@ def __init__(self, global_conf, app_conf, paths, **extra):
177181
raise ValueError("cassandra_seeds not set in the .ini")
178182
self.cassandra = PycassaConnectionPool('reddit',
179183
server_list = self.cassandra_seeds,
184+
pool_size = len(self.cassandra_seeds),
180185
# TODO: .ini setting
181186
timeout=15, max_retries=3,
182187
prefill=False)
@@ -365,6 +370,18 @@ def reset_caches():
365370
(self.reddit_host, self.reddit_pid,
366371
self.short_version, datetime.now()))
367372

373+
if self.LOGANS_RUN_LOW:
374+
minutes_to_live = random.randint(self.LOGANS_RUN_LOW,
375+
self.LOGANS_RUN_HIGH)
376+
self.logans_run_limit = datetime.now(self.tz) + timedelta(0,
377+
minutes_to_live * 60)
378+
disptime = self.logans_run_limit.astimezone(self.display_tz)
379+
self.log.info("Will shut down at %s (%d minutes from now)" %
380+
(disptime.strftime("%H:%M:%S"), minutes_to_live))
381+
else:
382+
self.logans_run_limit = None
383+
384+
368385
@staticmethod
369386
def to_bool(x):
370387
return (x.lower() == 'true') if x else None

r2/r2/lib/comment_tree.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def find_parents():
8080
for child in comment_tree[cur_cm]:
8181
stack.append(child)
8282

83+
new_parents = {}
8384
for comment in comments:
8485
cm_id = comment._id
8586
p_id = comment.parent_id
@@ -103,6 +104,7 @@ def find_parents():
103104

104105
#if this comment had a parent, find the parent's parents
105106
if p_id:
107+
new_parents[cm_id] = p_id
106108
for p_id in find_parents():
107109
num_children[p_id] += 1
108110

@@ -115,7 +117,18 @@ def find_parents():
115117

116118
for comment in comments:
117119
cm_id = comment._id
118-
r[cm_id] = p_id
120+
# print "In the olden days, I would have set %s -> %s" % (cm_id, p_id)
121+
122+
for cm_id, parent_id in new_parents.iteritems():
123+
# print "Now, I set %s -> %s" % (cm_id, parent_id)
124+
r[cm_id] = parent_id
125+
126+
for comment in comments:
127+
cm_id = comment._id
128+
if cm_id not in new_parents:
129+
r[cm_id] = None
130+
# print "And I set %s -> None" % cm_id
131+
119132
g.permacache.set(key, r)
120133

121134
g.permacache.set(comments_key(link_id),

r2/r2/lib/db/_sorts.pyx

+8-5
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,18 @@ cpdef double _confidence(int ups, int downs):
4141
"""The confidence sort.
4242
http://www.evanmiller.org/how-not-to-sort-by-average-rating.html"""
4343
cdef float n = ups + downs
44-
cdef float z
45-
cdef float phat
4644

4745
if n == 0:
4846
return 0
4947

50-
z = 1.0 #1.0 = 85%, 1.6 = 95%
51-
phat = float(ups) / n
52-
return sqrt(phat+z*z/(2*n)-z*((phat*(1-phat)+z*z/(4*n))/n))/(1+z*z/n)
48+
cdef float z = 1.281551565545 # 80% confidence
49+
cdef float p = float(ups) / n
50+
51+
left = p + 1/(2*n)*z*z
52+
right = z*sqrt(p*(1-p)/n + z*z/(4*n*n))
53+
under = 1+1/n*z*z
54+
55+
return (left - right) / under
5356

5457
cdef int up_range = 400
5558
cdef int down_range = 100

0 commit comments

Comments
 (0)