Merge commit 'origin/master'

This commit is contained in:
Mike
2009-05-15 12:46:25 -07:00
20 changed files with 151 additions and 222 deletions

View File

@@ -71,6 +71,7 @@ db_table_report_account_subreddit = relation, account, subreddit, main
###
timezone = UTC
lang = en
monitored_servers = localhost
#query cache settings
@@ -124,13 +125,14 @@ HOT_PAGE_AGE = 1
#
media_period = 10 minutes
rising_period = 12 hours
new_incubation = 90 seconds
# time of ratelimit purgatory (min)
RATELIMIT = 10
num_comments = 200
max_comments = 500
num_side_reddits = 20
num_default_reddits = 10
smtp_server = localhost
new_link_share_delay = 5 minutes

View File

@@ -34,7 +34,7 @@ from r2.lib.db import queries
from r2.lib.strings import Score
from r2.lib import organic
from r2.lib.solrsearch import SearchQuery
from r2.lib.utils import iters, check_cheating
from r2.lib.utils import iters, check_cheating, timeago
from r2.lib import sup
from admin import admin_profile_query
@@ -127,10 +127,14 @@ class ListingController(RedditController):
after = self.after,
count = self.count,
reverse = self.reverse,
keep_fn = self.keep_fn(),
wrap = self.builder_wrapper)
return b
def keep_fn(self):
return None
def listing(self):
"""Listing to generate from the builder"""
listing = LinkListing(self.builder_obj, show_nums = self.show_nums)
@@ -220,8 +224,8 @@ class HotController(FixListing, ListingController):
self.fix_listing = False
if c.site == Default:
user = c.user if c.user_is_loggedin else None
sr_ids = Subreddit.user_subreddits(user)
sr_ids = Subreddit.user_subreddits(c.user,
limit = g.num_default_reddits)
return normalized_hot(sr_ids)
#if not using the query_cache we still want cached front pages
elif (not g.use_query_cache
@@ -279,12 +283,34 @@ class NewController(ListingController):
def menus(self):
return [NewMenu(default = self.sort)]
def keep_fn(self):
def keep(item):
"""Avoid showing links that are too young, to give time
for things like the spam filter and thumbnail fetcher to
act on them before releasing them into the wild"""
wouldkeep = item.keep_item(item)
if c.user_is_loggedin and (c.user_is_admin or item.subreddit.is_moderator(c.user)):
# let admins and moderators see them regardless
return wouldkeep
elif wouldkeep and c.user_is_loggedin and c.user._id == item.author_id:
# also let the author of the link see them
return True
elif item._date > timeago(g.new_incubation):
# it's too young to show yet
return False
else:
# otherwise, fall back to the regular logic (don't
# show hidden links, etc)
return wouldkeep
return keep
def query(self):
if self.sort == 'rising':
return get_rising(c.site)
else:
return c.site.get_links('new', 'all')
@validate(sort = VMenu('controller', NewMenu))
def GET_listing(self, sort, **env):
self.sort = sort

View File

@@ -80,9 +80,12 @@ class UnloggedUser(FakeAccount):
if browser_langs:
lang = browser_langs[0]
content_langs = list(browser_langs)
# try to coerce the default language
if g.lang not in content_langs:
content_langs.append(g.lang)
content_langs.sort()
else:
lang = 'en'
lang = g.lang
content_langs = 'all'
self._defaults = self._defaults.copy()
self._defaults['pref_lang'] = lang
@@ -297,7 +300,7 @@ def set_iface_lang():
# (used for formatting of large numbers to break them up with ",").
# unfortunately, not directly compatible with gettext
locale.setlocale(locale.LC_ALL, g.locale)
lang = ['en']
lang = [g.lang]
# GET param wins
if c.host_lang:
lang = [c.host_lang]
@@ -316,7 +319,7 @@ def set_iface_lang():
break
except h.LanguageError:
#we don't have a translation for that language
h.set_lang('en', graceful_fail = True)
h.set_lang(g.lang, graceful_fail = True)
#TODO: add exceptions here for rtl languages
if c.lang in ('ar', 'he', 'fa'):

View File

@@ -181,7 +181,7 @@ class VLang(Validator):
if lang in g.all_languages:
return lang
#else
return 'en'
return g.lang
class VRequired(Validator):
def __init__(self, param, error, *a, **kw):

View File

@@ -25,7 +25,7 @@ import pytz, os, logging, sys, socket
from datetime import timedelta
from r2.lib.cache import LocalCache, Memcache, CacheChain
from r2.lib.db.stats import QueryStats
from r2.lib.translation import _get_languages
from r2.lib.translation import get_active_langs
from r2.lib.lock import make_lock_factory
from r2.lib.manager import db_manager
@@ -44,7 +44,7 @@ class Globals(object):
'RATELIMIT',
'num_comments',
'max_comments',
'num_side_reddits',
'num_default_reddits',
'num_query_queue_workers',
'max_sr_images',
]
@@ -131,7 +131,8 @@ class Globals(object):
self.REDDIT_MAIN = bool(os.environ.get('REDDIT_MAIN'))
# turn on for language support
self.languages, self.lang_name = _get_languages()
self.languages, self.lang_name = \
get_active_langs(default_lang= self.lang)
all_languages = self.lang_name.keys()
all_languages.sort()

View File

@@ -96,7 +96,7 @@ def insert_promoted(link_names, sr_ids, logged_in):
@memoize('cached_organic_links', time = organic_lifetime)
def cached_organic_links(user_id, langs):
if user_id is None:
sr_ids = Subreddit.default_srs(langs, ids = True)
sr_ids = Subreddit.default_subreddits()
else:
user = Account._byID(user_id, data=True)
sr_ids = Subreddit.user_subreddits(user)
@@ -137,7 +137,7 @@ def cached_organic_links(user_id, langs):
def organic_links(user):
from r2.controllers.reddit_base import organic_pos
sr_ids = Subreddit.user_subreddits(user, limit = None)
sr_ids = Subreddit.user_subreddits(user)
# make sure that these are sorted so the cache keys are constant
sr_ids.sort()

View File

@@ -726,12 +726,8 @@ class SubredditTopBar(Wrapped):
def __init__(self):
Wrapped.__init__(self)
my_reddits = []
sr_ids = Subreddit.user_subreddits(c.user if c.user_is_loggedin else None)
if sr_ids:
my_reddits = Subreddit._byID(sr_ids, True,
return_dict = False)
my_reddits.sort(key = lambda sr: sr.name.lower())
my_reddits = Subreddit.user_subreddits(c.user, ids = False)
my_reddits.sort(key = lambda sr: sr.name.lower())
drop_down_buttons = []
for sr in my_reddits:
@@ -748,8 +744,8 @@ class SubredditTopBar(Wrapped):
title = _('my reddits'),
type = 'srdrop')
pop_reddits = Subreddit.default_srs(c.content_langs, limit = 30)
pop_reddits = Subreddit.default_subreddits(ids = False,
limit = Subreddit.sr_limit)
buttons = [SubredditButton(sr) for sr in c.recent_reddits]
for sr in pop_reddits:
if sr not in c.recent_reddits:
@@ -758,57 +754,11 @@ class SubredditTopBar(Wrapped):
self.sr_bar = NavMenu(buttons, type='flatlist', separator = '-',
_id = 'sr-bar')
class SubredditBox(Wrapped):
"""A content pane that has the lists of subreddits that go in the
right pane by default"""
def __init__(self):
Wrapped.__init__(self)
self.title = _('Other reddit communities')
self.subtitle = 'Visit your subscribed reddits (in bold) or explore new ones'
self.create_link = ('/reddits/', menu.more)
self.more_link = ('/reddits/create', _('create'))
my_reddits = []
sr_ids = Subreddit.user_subreddits(c.user if c.user_is_loggedin else None)
if sr_ids:
my_reddits = Subreddit._byID(sr_ids, True,
return_dict = False)
my_reddits.sort(key = lambda sr: sr._downs, reverse = True)
display_reddits = my_reddits[:g.num_side_reddits]
#remove the current reddit
display_reddits = filter(lambda x: x != c.site, display_reddits)
pop_reddits = Subreddit.default_srs(c.content_langs, limit = g.num_side_reddits)
#add english reddits to the list
if c.content_langs != 'all' and 'en' not in c.content_langs:
en_reddits = Subreddit.default_srs(['en'])
pop_reddits += [sr for sr in en_reddits if sr not in pop_reddits]
for sr in pop_reddits:
if len(display_reddits) >= g.num_side_reddits:
break
if sr != c.site and sr not in display_reddits:
display_reddits.append(sr)
col1, col2 = [], []
cur_col, other = col1, col2
for sr in display_reddits:
cur_col.append((sr, sr in my_reddits))
cur_col, other = other, cur_col
self.cols = ((col1, col2))
self.mine = my_reddits
class SubscriptionBox(Wrapped):
"""The list of reddits a user is currently subscribed to to go in
the right pane."""
def __init__(self):
sr_ids = Subreddit.user_subreddits(c.user if c.user_is_loggedin else None)
srs = Subreddit._byID(sr_ids, True, return_dict = False)
srs = Subreddit.user_subreddits(c.user, ids = False)
srs.sort(key = lambda sr: sr.name.lower())
b = IDBuilder([sr._fullname for sr in srs])
self.reddits = LinkListing(b).listing().things

View File

@@ -28,6 +28,7 @@ hooks to the UI are the same.
"""
import helpers as h
from pylons import g
from pylons.i18n import _, ungettext
import random
@@ -220,14 +221,14 @@ class Score(object):
def fallback_trans(x):
"""For translating placeholder strings the user should never see
in raw form, such as 'funny 500 message'. If the string does not
translate in the current language, falls back on the 'en'
translate in the current language, falls back on the g.lang
translation that we've hopefully already provided"""
t = _(x)
if t == x:
l = h.get_lang()
h.set_lang('en', graceful_fail = True)
h.set_lang(g.lang, graceful_fail = True)
t = _(x)
if l and l[0] != 'en':
if l and l[0] != g.lang:
h.set_lang(l[0])
return t

View File

@@ -477,7 +477,8 @@ class Translator(LoggedSlots):
@classmethod
def get_slots(cls, locale = 'en'):
def get_slots(cls, locale = None):
locale = locale or pylons.g.lang
f = cls.outfile(locale, extension='data')
return LoggedSlots._get_slots(f)
@@ -571,7 +572,7 @@ def _rebuild_trans(path = _i18n_path):
t.save(compile = True)
def _get_languages(path = _i18n_path):
def get_active_langs(path = _i18n_path, default_lang = 'en'):
trans = []
trans_name = {}
for lang in os.listdir(path):
@@ -580,14 +581,15 @@ def _get_languages(path = _i18n_path):
name = Translator.get_name(lang)
trans_name[lang] = name
if Translator.is_enabled(lang) and Translator.in_use(lang):
# en is treated specially
if lang != 'en':
if lang != default_lang:
trans.append(lang)
if Translator.get_complete_frac(lang) < .5:
name += ' (*)'
trans.sort()
trans.insert(0, 'en')
trans_name['en'] = "English"
# insert the default language at the top of the list
trans.insert(0, default_lang)
if default_lang not in trans_name:
trans_name[default_lang] = default_lang
return trans, trans_name

View File

@@ -1030,3 +1030,10 @@ def common_subdomain(domain1, domain2):
if domain2.endswith(d):
return d
return ""
def interleave_lists(*args):
max_len = max(len(x) for x in args)
for i in xrange(max_len):
for a in args:
if i < len(a):
yield a[i]

View File

@@ -46,8 +46,8 @@ class Account(Thing):
pref_min_link_score = -4,
pref_min_comment_score = -4,
pref_num_comments = g.num_comments,
pref_lang = 'en',
pref_content_langs = ('en',),
pref_lang = g.lang,
pref_content_langs = (g.lang,),
pref_over_18 = False,
pref_compress = False,
pref_organic = True,

View File

@@ -257,6 +257,7 @@ class Link(Thing, Printable):
from r2.lib.count import incr_counts
from r2.lib.media import thumbnail_url
from r2.lib.utils import timeago
from r2.lib.template_helpers import get_domain
saved = Link._saved(user, wrapped) if user else {}
hidden = Link._hidden(user, wrapped) if user else {}
@@ -317,6 +318,17 @@ class Link(Thing, Printable):
else:
item.nofollow = False
item.subreddit_path = item.subreddit.path
if c.cname:
item.subreddit_path = ("http://" +
get_domain(cname = (c.site == item.subreddit),
subreddit = False))
if c.site != item.subreddit:
item.subreddit_path += item.subreddit.path
item.domain_path = "/domain/%s" % item.domain
if item.is_self:
item.domain_path = item.subreddit_path
if c.user_is_loggedin:
incr_counts(wrapped)

View File

@@ -30,7 +30,7 @@ from printable import Printable
from r2.lib.db.userrel import UserRel
from r2.lib.db.operators import lower, or_, and_, desc
from r2.lib.memoize import memoize
from r2.lib.utils import tup
from r2.lib.utils import tup, interleave_lists
from r2.lib.strings import strings, Score
from r2.lib.filters import _force_unicode
@@ -58,7 +58,7 @@ class Subreddit(Thing, Printable):
sr_limit = 50
@classmethod
def _new(self, name, title, author_id, ip, lang = 'en', type = 'public',
def _new(self, name, title, author_id, ip, lang = g.lang, type = 'public',
over_18 = False, **kw):
with g.make_lock('create_sr_' + name.lower()):
try:
@@ -243,7 +243,7 @@ class Subreddit(Thing, Printable):
def add_props(cls, user, wrapped):
names = ('subscriber', 'moderator', 'contributor')
rels = (SRMember._fast_query(wrapped, [user], names) if user else {})
defaults = Subreddit.default_srs(c.content_langs, ids = True)
defaults = Subreddit.default_subreddits()
for item in wrapped:
if not user or not user.has_subscribed:
item.subscriber = item._id in defaults
@@ -276,10 +276,11 @@ class Subreddit(Thing, Printable):
#return 1
@classmethod
def default_srs(cls, lang, ids = False, limit = 10):
def top_lang_srs(cls, lang, limit):
"""Returns the default list of subreddits for a given language, sorted
by popularity"""
pop_reddits = Subreddit._query(Subreddit.c.type == ('public', 'restricted'),
pop_reddits = Subreddit._query(Subreddit.c.type == ('public',
'restricted'),
sort=desc('_downs'),
limit = limit,
data = True,
@@ -292,36 +293,71 @@ class Subreddit(Thing, Printable):
if not c.over18:
pop_reddits._filter(Subreddit.c.over_18 == False)
pop_reddits = list(pop_reddits)
if not pop_reddits and lang != 'en':
pop_reddits = cls.default_srs('en')
return [s._id for s in pop_reddits] if ids else list(pop_reddits)
return list(pop_reddits)
@classmethod
def user_subreddits(cls, user, limit = sr_limit):
"""subreddits that appear in a user's listings. returns the default
srs if there are no subscriptions."""
def default_subreddits(cls, ids = True, limit = g.num_default_reddits):
"""
Generates a list of the subreddits any user with the current
set of language preferences and no subscriptions would see.
An optional kw argument 'limit' is defaulted to g.num_default_reddits
"""
langs = list(c.content_langs)
# g.lang will be in the the current set of content langs
# unless the user has explicity removed it. Since most
# content is in g.lang, set the subreddit ratio to 50/50.
if limit and langs != 'all' and g.lang in langs and len(langs) != 1:
# lookup default lang subreddits
default_srs = cls.top_lang_srs([g.lang], limit)
# remove g.lang from conten_lang list and use it to
# grab content_lang subreddits
langs.remove(g.lang)
lang_srs = cls.top_lang_srs(langs, limit)
# interleave the two lists, putting the lang ones first
srs = list(interleave_lists(lang_srs, default_srs))
if limit:
srs = srs[:limit]
else:
# the user knows better and has set their langs accordingly
srs = cls.top_lang_srs(c.content_langs, limit)
return [s._id for s in srs] if ids else srs
@classmethod
def user_subreddits(cls, user, ids = True, limit = sr_limit):
"""
subreddits that appear in a user's listings. If the user has
subscribed, returns the stored set of subscriptions.
Otherwise, return the default set.
"""
# note: for user not logged in, the fake user account has
# has_subscribed == False by default.
if user and user.has_subscribed:
sr_ids = Subreddit.reverse_subscriber_ids(user)
if limit and len(sr_ids) > limit:
return random.sample(sr_ids, limit)
else:
return sr_ids
sr_ids = random.sample(sr_ids, limit)
return sr_ids if ids else Subreddit._byID(sr_ids, True, False)
else:
return cls.default_srs(c.content_langs, ids = True)
# if there is a limit, we want *at most* limit subreddits.
# Allow the default_subreddit list to return the number it
# would normally and then slice.
srs = cls.default_subreddits(ids = ids)
if limit:
srs = srs[:limit]
return srs
def is_subscriber_defaults(self, user):
if user.has_subscribed:
return self.is_subscriber(user)
else:
return self in self.default_srs(c.content_langs)
return self in self.default_subreddits(ids = False)
@classmethod
def subscribe_defaults(cls, user):
if not user.has_subscribed:
for sr in Subreddit.default_srs(c.content_langs):
for sr in cls.user_subreddits(None, False,
limit = g.num_default_reddits):
#this will call reverse_subscriber_ids after every
#addition. if it becomes a problem we should make an
#add_multiple_subscriber fn
@@ -334,9 +370,7 @@ class Subreddit(Thing, Printable):
def submit_sr_names(cls, user):
"""subreddit names that appear in a user's submit page. basically a
sorted/rearranged version of user_subreddits()."""
sub_ids = cls.user_subreddits(user, False)
srs = Subreddit._byID(sub_ids, True,
return_dict = False)
srs = cls.user_subreddits(user, ids = False)
names = [s.name for s in srs if s.can_submit(user)]
names.sort()

View File

@@ -281,45 +281,6 @@ ul.flat-vert {text-align: left;}
margin: 7px 0;
}
.subredditbox {
border: 1px solid gray;
}
.subredditbox h4 {
font-size: 170%;
color: #333;
padding: 5px 0 0 5px;
}
.subredditbox h5 {
font-weight: normal;
color: dimgray;
font-size: 110%;
padding-left: 5px;
}
.subredditbox input[type=submit] {
border: none;
background-color: white;
text-decoration: underline;
cursor: pointer;
margin-left: 10px;
color: #369;
}
.subredditbox input[type=checkbox] {margin-right: 5px;}
.subredditbox ul {
padding: 5px;
width: 139px;
list-style-type: none;
float: left;
}
.subredditbox li {padding: 4px; text-transform: uppercase;}
.subredditbox li a {color: #369;}
.subredditbox li.selected a{font-weight: bold;}
.subredditbox .spacer { margin: 5px 0 }
.morelink {
background-color:#FFFFFF;
color:#369;

View File

@@ -140,7 +140,7 @@ function update_title(elem) {
<%
default_lang = thing.site and thing.site.lang or c.lang or ''
default_lang = default_lang.split('-')[0]
default_lang = 'en' if len(default_lang) != 2 else default_lang
default_lang = g.lang if len(default_lang) != 2 else default_lang
%>
${language_tool(all_langs = True, default_lang = default_lang)}
</td>

View File

@@ -26,6 +26,7 @@
%>
<%inherit file="printable.html"/>
<%namespace file="utils.html" import="plain_link" />
<%def name="numcol()">
<% num = thing.num %>
@@ -81,18 +82,8 @@
</%def>
<%def name="subreddit()" buffered="True">
<%
if c.cname:
path = "http://" + get_domain(cname = (c.site == thing.subreddit),
subreddit = (c.site != thing.subreddit))
else:
path = thing.subreddit.path
endif
%>
<a href="${path}" class="subreddit hover"
${'target="_top"' if c.cname else ''} >
${thing.subreddit.name}
</a>
${plain_link(thing.subreddit.name, thing.subreddit_path, sr_path = False,
cname = False, _class = "subreddit hover")}
<script type="text/javascript">
reddit.sr['${thing._fullname}'] = '${thing.subreddit._fullname}';
</script>
@@ -115,8 +106,8 @@
<%def name="domain()">
<span class="domain">
(<a href="/domain/${thing.domain}">${thing.domain}</a>)
</span>
(${plain_link(thing.domain, thing.domain_path, _sr_path = False)})
</span>
</%def>
<%def name="tagline()">

View File

@@ -69,7 +69,7 @@
else:
action = "/post/unlogged_options"
if not c.frameless_cname:
action = add_sr(action, nocname=True)
action = add_sr(action, sr_path = False, nocname=True)
%>
<form action="${action}" method="post" class="pretty-form short-text">
<input type="hidden" name="uh" value="${c.modhash}" />

View File

@@ -76,7 +76,7 @@
<div class="popup">
<%include file="prefoptions.html" />
<div style="text-align:center; clear:both;">
<a href="#" onclick="return hidecover('langcover','langpopup')">
<a href="#" onclick="return hidecover(this)">
${_("close this window")}
</a>
</div>

View File

@@ -1,61 +0,0 @@
## 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 CondeNet, Inc.
##
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
## CondeNet, Inc. All Rights Reserved.
################################################################################
<%namespace file="utils.html" import="plain_link"/>
<div class="subredditbox">
<form method="post" action="/post/subscriptions" id="sr_subscriptions"
%if c.user_is_loggedin:
onsubmit="return post_form(this, 'subscriptions');"
%else:
onsubmit="return showcover(true, 'sr_change_');"
%endif
>
<h4>
${thing.title}
<input type="hidden" name="dest" value="${request.fullpath}"/>
<input type="submit" name="submit"
value="${_('save edits')}" id="subscr_sub"
style="display: none" />
</h4>
<h5>${thing.subtitle}</h5>
%for col in thing.cols:
%if col:
<ul>
%for sr, sel in col:
<li ${"class='selected'" if sel else ''} >
<%
if sr.path:
path = sr.path
else:
path = '/'
%>
${plain_link(sr.name, path, _sr_path=False, nocname=True)}
</li>
%endfor
</ul>
%endif
%endfor
<div class="clear"></div>
</form>
${plain_link(unsafe('%s &raquo;' % _("more")), "/reddits/", _class="morelink",
fmt='%s', _sr_path=False, nocname=True)}
</div>

View File

@@ -151,7 +151,7 @@ ${unsafe(txt)}
<%def name="language_tool(name='lang', allow_blank = False,
default_lang = 'en',
default_lang = g.lang,
show_regions = False,
all_langs = False)">
<%