mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-04-27 03:00:12 -04:00
1. Allow a reddit to have a cname, like www.proggit.com, that renders
the listing for that reddit 2. Allow a reddit to have a custom CSS stylesheet that appears to visitors 3. Allow a reddit to upload a custom reddit alien logo
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -14,6 +14,7 @@
|
||||
.hgignore
|
||||
lighttpd.**
|
||||
development.ini
|
||||
development_*.ini
|
||||
production.ini
|
||||
r2/r2/public/static/frame.js
|
||||
r2/r2/public/static/reddit.js
|
||||
|
||||
@@ -64,6 +64,8 @@ write_query_queue = False
|
||||
stylesheet = reddit.css
|
||||
stylesheet_rtl = reddit_rtl.css
|
||||
|
||||
allowed_css_linked_domains = my.domain.com, my.otherdomain.com
|
||||
|
||||
login_cookie = reddit_session
|
||||
domain = localhost
|
||||
default_sr = localhost
|
||||
|
||||
@@ -173,11 +173,24 @@ class DomainMiddleware(object):
|
||||
def __call__(self, environ, start_response):
|
||||
# get base domain as defined in INI file
|
||||
base_domain = config['global_conf']['domain']
|
||||
|
||||
sub_domains = environ['HTTP_HOST']
|
||||
if not sub_domains.endswith(base_domain):
|
||||
#if the domain doesn't end with base_domain, don't do anything
|
||||
return self.app(environ, start_response)
|
||||
try:
|
||||
sub_domains, request_port = environ['HTTP_HOST'].split(':')
|
||||
environ['request_port'] = int(request_port)
|
||||
except ValueError:
|
||||
sub_domains = environ['HTTP_HOST'].split(':')[0]
|
||||
|
||||
#If the domain doesn't end with base_domain, assume
|
||||
#this is a cname, and redirect to the frame controller.
|
||||
#Ignore localhost so paster shell still works.
|
||||
#If this is an error, don't redirect
|
||||
if (not sub_domains.endswith(base_domain)
|
||||
and (not sub_domains == 'localhost')):
|
||||
environ['sub_domain'] = sub_domains
|
||||
if (not environ.get('extension')
|
||||
and (not environ['PATH_INFO'].startswith('/error'))):
|
||||
environ['original_path'] = environ['PATH_INFO']
|
||||
environ['PATH_INFO'] = '/frame'
|
||||
return self.app(environ, start_response)
|
||||
|
||||
sub_domains = sub_domains[:-len(base_domain)].strip('.')
|
||||
sub_domains = sub_domains.split('.')
|
||||
@@ -294,6 +307,22 @@ class RequestLogMiddleware(object):
|
||||
pass
|
||||
return r
|
||||
|
||||
class LimitUploadSize(object):
|
||||
def __init__(self, app, max_size=1024*500):
|
||||
self.app = app
|
||||
self.max_size = max_size
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
cl_key = 'CONTENT_LENGTH'
|
||||
if environ['REQUEST_METHOD'] == 'POST':
|
||||
if ((cl_key not in environ)
|
||||
or int(environ[cl_key]) > self.max_size):
|
||||
r = Response()
|
||||
r.status_code = 500
|
||||
r.content = 'request too big'
|
||||
return r(environ, start_response)
|
||||
|
||||
return self.app(environ, start_response)
|
||||
|
||||
#god this shit is disorganized and confusing
|
||||
class RedditApp(PylonsBaseWSGIApp):
|
||||
@@ -335,13 +364,15 @@ def make_app(global_conf, full_stack=True, **app_conf):
|
||||
|
||||
# CUSTOM MIDDLEWARE HERE (filtered by the error handling middlewares)
|
||||
|
||||
app = LimitUploadSize(app)
|
||||
app = ProfilingMiddleware(app)
|
||||
app = SourceViewMiddleware(app)
|
||||
|
||||
app = DomainMiddleware(app)
|
||||
app = SubredditMiddleware(app)
|
||||
app = DomainMiddleware(app)
|
||||
app = ExtensionMiddleware(app)
|
||||
|
||||
|
||||
log_path = global_conf.get('log_path')
|
||||
if log_path:
|
||||
process_iden = global_conf.get('scgi_port', 'default')
|
||||
|
||||
@@ -94,6 +94,8 @@ def make_map(global_conf={}, app_conf={}):
|
||||
|
||||
mc('/mail/optout', controller='front', action = 'optout')
|
||||
mc('/mail/optin', controller='front', action = 'optin')
|
||||
mc('/stylesheet', controller = 'front', action = 'stylesheet')
|
||||
mc('/frame', controller='front', action = 'frame')
|
||||
|
||||
mc('/', controller='hot', action='listing')
|
||||
|
||||
|
||||
@@ -27,12 +27,13 @@ from pylons import c, request
|
||||
from validator import *
|
||||
|
||||
from r2.models import *
|
||||
from r2.models.subreddit import Default as DefaultSR
|
||||
import r2.models.thing_changes as tc
|
||||
|
||||
from r2.lib.utils import get_title, sanitize_url, timeuntil, set_last_modified
|
||||
from r2.lib.wrapped import Wrapped
|
||||
from r2.lib.pages import FriendList, ContributorList, ModList, \
|
||||
BannedList, BoringPage, FormPage, NewLink
|
||||
BannedList, BoringPage, FormPage, NewLink, CssError, UploadedImage
|
||||
|
||||
from r2.lib.menus import CommentSortMenu
|
||||
from r2.lib.translation import Translator
|
||||
@@ -40,15 +41,18 @@ from r2.lib.normalized_hot import expire_hot
|
||||
from r2.lib.captcha import get_iden
|
||||
from r2.lib import emailer
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.memoize import clear_memo
|
||||
from r2.lib.filters import _force_unicode
|
||||
from r2.lib.db import queries
|
||||
from r2.config import cache
|
||||
from r2.lib.jsonresponse import JsonResponse, Json
|
||||
from r2.lib.jsontemplates import api_type
|
||||
from r2.lib import cssfilter
|
||||
|
||||
from simplejson import dumps
|
||||
|
||||
from r2.lib.jsonresponse import JsonResponse, Json
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from md5 import md5
|
||||
from r2.lib.organic import update_pos
|
||||
|
||||
def link_listing_by_url(url, count = None):
|
||||
@@ -64,7 +68,7 @@ def link_listing_by_url(url, count = None):
|
||||
builder = IDBuilder(names, num = 25)
|
||||
listing = LinkListing(builder).listing()
|
||||
return listing
|
||||
|
||||
|
||||
|
||||
class ApiController(RedditController):
|
||||
def response_func(self, **kw):
|
||||
@@ -74,9 +78,9 @@ class ApiController(RedditController):
|
||||
try:
|
||||
l = Link._by_url(url, sr)
|
||||
if message:
|
||||
return l.permalink + '?already_submitted=true'
|
||||
return l.already_submitted_link()
|
||||
else:
|
||||
return l.permalink
|
||||
return l.make_permalink_slow()
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
@@ -272,7 +276,7 @@ class ApiController(RedditController):
|
||||
# well, nothing left to do but submit it
|
||||
l = Link._submit(request.post.title, url, c.user, sr, ip, spam)
|
||||
if url.lower() == 'self':
|
||||
l.url = l.permalink
|
||||
l.url = l.make_permalink_slow()
|
||||
l.is_self = True
|
||||
l._commit()
|
||||
Vote.vote(c.user, l, True, ip, spam)
|
||||
@@ -292,8 +296,16 @@ class ApiController(RedditController):
|
||||
|
||||
# flag search indexer that something has changed
|
||||
tc.changed(l)
|
||||
|
||||
# make_permalink is designed for links that can be set to _top
|
||||
# here, we need to generate an ajax redirect as if we were not on a
|
||||
# cname.
|
||||
cname = c.cname
|
||||
c.cname = False
|
||||
path = l.make_permalink_slow()
|
||||
c.cname = cname
|
||||
|
||||
res._redirect(l.permalink)
|
||||
res._redirect(path)
|
||||
|
||||
|
||||
def _login(self, res, user, dest='', rem = None):
|
||||
@@ -719,6 +731,109 @@ class ApiController(RedditController):
|
||||
# flag search indexer that something has changed
|
||||
tc.changed(thing)
|
||||
|
||||
@Json
|
||||
@validate(VUser(),
|
||||
VModhash(),
|
||||
stylesheet_contents = nop('stylesheet_contents'),
|
||||
op = VOneOf('op',['save','preview']))
|
||||
def POST_subreddit_stylesheet(self, res, stylesheet_contents = '', op='save'):
|
||||
if not c.site.can_change_stylesheet(c.user):
|
||||
return self.abort(403,'forbidden')
|
||||
|
||||
if g.css_killswitch:
|
||||
return self.abort(403,'forbidden')
|
||||
|
||||
parsed, report = cssfilter.validate_css(stylesheet_contents)
|
||||
|
||||
if report.errors:
|
||||
error_items = [ CssError(x).render(style='html')
|
||||
for x in sorted(report.errors) ]
|
||||
|
||||
res._update('status', innerHTML = _('validation errors'))
|
||||
res._update('validation-errors', innerHTML = ''.join(error_items))
|
||||
res._show('error-header')
|
||||
else:
|
||||
res._hide('error-header')
|
||||
res._update('status', innerHTML = '')
|
||||
res._update('validation-errors', innerHTML = '')
|
||||
|
||||
if not report.errors and op == 'save':
|
||||
stylesheet_contents_user = stylesheet_contents
|
||||
stylesheet_contents_parsed = parsed.cssText if parsed else ''
|
||||
|
||||
c.site.stylesheet_contents = stylesheet_contents_parsed
|
||||
c.site.stylesheet_contents_user = stylesheet_contents_user
|
||||
|
||||
c.site.stylesheet_hash = md5(stylesheet_contents_parsed).hexdigest()
|
||||
|
||||
set_last_modified(c.site,'stylesheet_contents')
|
||||
tc.changed(c.site)
|
||||
c.site._commit()
|
||||
|
||||
res._update('status', innerHTML = 'saved')
|
||||
res._call('applyStylesheetFromTextbox("stylesheet_contents");')
|
||||
res._update('validation-errors', innerHTML = '')
|
||||
|
||||
elif op == 'preview':
|
||||
# try to find a link to use, otherwise give up and
|
||||
# return
|
||||
links = cssfilter.find_preview_links(c.site)
|
||||
if not links:
|
||||
# we're probably not going to be able to find any
|
||||
# comments, either; screw it
|
||||
return
|
||||
|
||||
res._show('preview-table')
|
||||
|
||||
# do a regular link
|
||||
cssfilter.rendered_link('preview_link_normal',
|
||||
res, links,
|
||||
media = 'off', compress=False)
|
||||
# now do one with media
|
||||
cssfilter.rendered_link('preview_link_media',
|
||||
res, links,
|
||||
media = 'on', compress=False)
|
||||
# do a compressed link
|
||||
cssfilter.rendered_link('preview_link_compressed',
|
||||
res, links,
|
||||
media = 'off', compress=True)
|
||||
# and do a comment
|
||||
comments = cssfilter.find_preview_comments(c.site)
|
||||
if not comments:
|
||||
return
|
||||
cssfilter.rendered_comment('preview_comment',res,comments)
|
||||
|
||||
@validate(VUser(),
|
||||
VModhash(),
|
||||
VRatelimit(rate_user = True,
|
||||
rate_ip = True,
|
||||
prefix = 'upload_reddit_img_'),
|
||||
file = VLength('file',length=1024*500),
|
||||
op = VOneOf('op',['upload','delete']))
|
||||
def POST_upload_header_img(self, file, op):
|
||||
if not c.site.can_change_stylesheet(c.user):
|
||||
return self.abort403()
|
||||
|
||||
if g.css_killswitch:
|
||||
return self.abort(403,'forbidden')
|
||||
|
||||
if op == 'upload':
|
||||
try:
|
||||
cleaned = cssfilter.clean_image(file,'PNG')
|
||||
new_url = cssfilter.save_header_image(c.site, cleaned)
|
||||
except cssfilter.BadImage:
|
||||
return UploadedImage(_('bad image'),c.site.header).render()
|
||||
|
||||
c.site.header = new_url
|
||||
c.site._commit()
|
||||
|
||||
return UploadedImage(_('saved'),new_url,'upload').render()
|
||||
elif op == 'delete':
|
||||
c.site.header = None
|
||||
c.site._commit()
|
||||
|
||||
return UploadedImage(_('deleted'),DefaultSR.header,'delete').render()
|
||||
|
||||
@Json
|
||||
@validate(VUser(),
|
||||
VModhash(),
|
||||
@@ -727,6 +842,7 @@ class ApiController(RedditController):
|
||||
prefix = 'create_reddit_'),
|
||||
name = VSubredditName("name"),
|
||||
title = VSubredditTitle("title"),
|
||||
domain = VCnameDomain("domain"),
|
||||
description = VSubredditDesc("description"),
|
||||
firsttext = nop("firsttext"),
|
||||
header = nop("headerfile"),
|
||||
@@ -740,11 +856,10 @@ class ApiController(RedditController):
|
||||
type = VOneOf('type', ('public', 'private', 'restricted'))
|
||||
)
|
||||
def POST_site_admin(self, res, name ='', sr = None, **kw):
|
||||
res._update('status', innerHTML = '')
|
||||
redir = False
|
||||
kw = dict((k, v) for k, v in kw.iteritems()
|
||||
if v is not None
|
||||
and k in ('name', 'title', 'description', 'firsttext',
|
||||
and k in ('name', 'title', 'domain', 'description', 'firsttext',
|
||||
'static_path', 'ad_file', 'over_18', 'show_media',
|
||||
'type', 'header', 'lang', 'stylesheet'))
|
||||
|
||||
@@ -753,6 +868,12 @@ class ApiController(RedditController):
|
||||
time = timeuntil(datetime.now(g.tz) + timedelta(seconds=600))
|
||||
c.errors.add(errors.RATELIMIT, {'time': time})
|
||||
|
||||
domain = kw['domain']
|
||||
cname_sr = domain and Subreddit._by_domain(domain)
|
||||
if cname_sr and (not sr or sr != cname_sr):
|
||||
kw['domain'] = None
|
||||
c.errors.add(errors.USED_CNAME)
|
||||
|
||||
if not sr and res._chk_error(errors.RATELIMIT):
|
||||
pass
|
||||
elif not sr and res._chk_errors((errors.SUBREDDIT_EXISTS,
|
||||
@@ -764,6 +885,9 @@ class ApiController(RedditController):
|
||||
res._focus('title')
|
||||
elif res._chk_error(errors.INVALID_SUBREDDIT_TYPE):
|
||||
pass
|
||||
elif res._chk_errors((errors.BAD_CNAME, errors.USED_CNAME)):
|
||||
res._hide('example_domain')
|
||||
res._focus('domain')
|
||||
elif res._chk_error(errors.DESC_TOO_LONG):
|
||||
res._focus('description')
|
||||
|
||||
@@ -784,6 +908,7 @@ class ApiController(RedditController):
|
||||
|
||||
if not res.error:
|
||||
#assume sr existed, or was just built
|
||||
clear_memo('subreddit._by_domain', Subreddit, _force_unicode(sr.domain))
|
||||
for k, v in kw.iteritems():
|
||||
setattr(sr, k, v)
|
||||
sr._commit()
|
||||
@@ -791,6 +916,9 @@ class ApiController(RedditController):
|
||||
# flag search indexer that something has changed
|
||||
tc.changed(sr)
|
||||
|
||||
res._update('status', innerHTML = _('saved'))
|
||||
|
||||
|
||||
if redir:
|
||||
res._redirect(redir)
|
||||
|
||||
|
||||
@@ -98,8 +98,13 @@ class ErrorController(RedditController):
|
||||
try:
|
||||
return RedditController.__call__(self, environ, start_response)
|
||||
except:
|
||||
c.response.content = "something really awful just happened"
|
||||
return c.response
|
||||
if g.debug:
|
||||
# if we're in debug mode, let this hit Pylons so we
|
||||
# get a stack trace
|
||||
raise
|
||||
else:
|
||||
c.response.content = "something really awful just happened"
|
||||
return c.response
|
||||
|
||||
|
||||
def send403(self):
|
||||
|
||||
@@ -57,6 +57,8 @@ error_list = dict((
|
||||
('DRACONIAN', _('you must accept the terms first')),
|
||||
('BANNED_IP', "IP banned"),
|
||||
('BANNED_DOMAIN', "Domain banned"),
|
||||
('BAD_CNAME', "that domain isn't going to work"),
|
||||
('USED_CNAME', "that cname is already in use"),
|
||||
('INVALID_SUBREDDIT_TYPE', _('that option is not valid')),
|
||||
('DESC_TOO_LONG', _('description is too long')),
|
||||
('CHEATER', 'what do you think you\'re doing there?'),
|
||||
|
||||
@@ -154,7 +154,7 @@ class FrontController(RedditController):
|
||||
|
||||
# if permalink page, add that message first to the content
|
||||
if comment:
|
||||
displayPane.append(PermalinkMessage(article.permalink))
|
||||
displayPane.append(PermalinkMessage(article.make_permalink_slow()))
|
||||
|
||||
# insert reply box only for logged in user
|
||||
if c.user_is_loggedin and article.subreddit_slow.can_comment(c.user):
|
||||
@@ -328,10 +328,21 @@ class FrontController(RedditController):
|
||||
).render()
|
||||
return res
|
||||
|
||||
def GET_stylesheet(self):
|
||||
if hasattr(c.site,'stylesheet_contents') and not g.css_killswitch:
|
||||
self.check_modified(c.site,'stylesheet_contents')
|
||||
|
||||
c.response.content = c.site.stylesheet_contents
|
||||
c.response_content_type = 'text/css'
|
||||
|
||||
return c.response
|
||||
else:
|
||||
return self.abort404()
|
||||
|
||||
@base_listing
|
||||
@validate(location = nop('location'))
|
||||
def GET_editreddit(self, location, num, after, reverse, count):
|
||||
"""Edit reddit form. """
|
||||
"""Edit reddit form."""
|
||||
if isinstance(c.site, FakeSubreddit):
|
||||
return self.abort404()
|
||||
|
||||
@@ -346,6 +357,17 @@ class FrontController(RedditController):
|
||||
pane = BannedList(editable = is_moderator)
|
||||
elif location == 'contributors' and c.site.type != 'public':
|
||||
pane = ContributorList(editable = is_moderator)
|
||||
elif (location == 'stylesheet'
|
||||
and c.site.can_change_stylesheet(c.user)
|
||||
and not g.css_killswitch):
|
||||
if hasattr(c.site,'stylesheet_contents_user') and c.site.stylesheet_contents_user:
|
||||
stylesheet_contents = c.site.stylesheet_contents_user
|
||||
elif hasattr(c.site,'stylesheet_contents') and c.site.stylesheet_contents:
|
||||
stylesheet_contents = c.site.stylesheet_contents
|
||||
else:
|
||||
stylesheet_contents = ''
|
||||
pane = SubredditStylesheet(site = c.site,
|
||||
stylesheet_contents = stylesheet_contents)
|
||||
elif is_moderator and location == 'spam':
|
||||
links = Link._query(Link.c._spam == True)
|
||||
comments = Comment._query(Comment.c._spam == True)
|
||||
@@ -486,6 +508,10 @@ class FrontController(RedditController):
|
||||
"""wipe login cookie and redirect to referer."""
|
||||
self.logout()
|
||||
dest = request.referer or '/'
|
||||
if c.cname:
|
||||
dest = '/?cnameframe=1'
|
||||
if not dest.startswith("http://"):
|
||||
return self.redirect(c.site.path + dest)
|
||||
return self.redirect(dest)
|
||||
|
||||
|
||||
@@ -591,3 +617,14 @@ class FrontController(RedditController):
|
||||
ApiController.POST_optin."""
|
||||
return self._render_opt_in_out(msg_hash, False)
|
||||
|
||||
def GET_frame(self):
|
||||
"""used for cname support. makes a frame and
|
||||
puts the proper url as the frame source"""
|
||||
sub_domain = request.environ.get('sub_domain')
|
||||
original_path = request.environ.get('original_path')
|
||||
sr = Subreddit._by_domain(sub_domain)
|
||||
if sub_domain and sr and original_path:
|
||||
return Cnameframe(original_path, sr.name, sr.title, sub_domain).render()
|
||||
else:
|
||||
return self.abort404()
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ class MessageController(ListingController):
|
||||
@staticmethod
|
||||
def builder_wrapper(thing):
|
||||
if isinstance(thing, Comment):
|
||||
p = thing.permalink
|
||||
p = thing.make_permalink_slow()
|
||||
f = thing._fullname
|
||||
thing.__class__ = Message
|
||||
w = Wrapped(thing)
|
||||
|
||||
@@ -101,10 +101,14 @@ class PostController(ApiController):
|
||||
pref_min_comment_score = VInt('min_comment_score', -100, 100),
|
||||
pref_num_comments = VInt('num_comments', 1, g.max_comments,
|
||||
default = g.num_comments),
|
||||
pref_show_stylesheets = VBoolean('show_stylesheets'),
|
||||
all_langs = nop('all-langs', default = 'all'))
|
||||
def POST_options(self, all_langs, pref_lang, **kw):
|
||||
self.set_options(all_langs, pref_lang, **kw)
|
||||
return self.redirect("/prefs?done=true")
|
||||
q_string = {'done': 'true'}
|
||||
if c.cname:
|
||||
q_string['cnameframe'] = '1'
|
||||
return self.redirect((request.referer or "/prefs") + query_string(q_string))
|
||||
|
||||
def GET_over18(self):
|
||||
return BoringPage(_("over 18?"),
|
||||
|
||||
@@ -34,7 +34,7 @@ import r2.config as config
|
||||
from r2.models import *
|
||||
from errors import ErrorSet
|
||||
from validator import *
|
||||
from r2.lib.template_helpers import reddit_link
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.jsontemplates import api_type
|
||||
|
||||
from copy import copy
|
||||
@@ -105,7 +105,7 @@ def set_user_cookie(name, val):
|
||||
uname = c.user.name if c.user_is_loggedin else ""
|
||||
c.response.set_cookie(uname + '_' + name,
|
||||
value = val,
|
||||
domain = c.domain)
|
||||
domain = g.domain)
|
||||
|
||||
def read_click_cookie():
|
||||
if c.user_is_loggedin:
|
||||
@@ -129,7 +129,7 @@ def firsttime():
|
||||
if not request.cookies.get("reddit_first"):
|
||||
c.response.set_cookie("reddit_first", "first",
|
||||
expires = NEVER,
|
||||
domain = c.domain)
|
||||
domain = g.domain)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -146,7 +146,9 @@ def set_subreddit():
|
||||
sr_name=request.environ.get("subreddit", request.params.get('r'))
|
||||
|
||||
if not sr_name or sr_name == Default.name:
|
||||
c.site = Default
|
||||
sub_domain = request.environ.get('sub_domain')
|
||||
sr = Subreddit._by_domain(sub_domain) if sub_domain else None
|
||||
c.site = sr or Default
|
||||
elif sr_name == 'r':
|
||||
c.site = Sub
|
||||
else:
|
||||
@@ -191,6 +193,12 @@ def set_content_type():
|
||||
c.response_wrappers.append(utils.to_js)
|
||||
elif extension == 'mobile':
|
||||
c.render_style = 'mobile'
|
||||
#Insert new extentions above this line
|
||||
elif extension not in ('', 'html'):
|
||||
dest = "http://%s%s" % (request.host, request.path)
|
||||
if request.get:
|
||||
dest += utils.query_string(request.get)
|
||||
redirect_to(dest)
|
||||
|
||||
def get_browser_langs():
|
||||
browser_langs = []
|
||||
@@ -252,6 +260,11 @@ def set_content_lang():
|
||||
else:
|
||||
c.content_langs = c.user.pref_content_langs
|
||||
|
||||
def set_cnameframe():
|
||||
if (bool(request.params.get('cnameframe'))
|
||||
or request.host.split(":")[0] != g.domain):
|
||||
c.cname = True
|
||||
|
||||
def ratelimit_agents():
|
||||
user_agent = request.user_agent
|
||||
for s in g.agents:
|
||||
@@ -308,6 +321,7 @@ class RedditController(BaseController):
|
||||
key = ''.join((str(c.lang),
|
||||
str(c.content_langs),
|
||||
request.host,
|
||||
c.cname,
|
||||
request.fullpath,
|
||||
str(c.firsttime),
|
||||
str(c.over18)))
|
||||
@@ -367,14 +381,15 @@ class RedditController(BaseController):
|
||||
set_content_type()
|
||||
set_iface_lang()
|
||||
set_content_lang()
|
||||
set_cnameframe()
|
||||
|
||||
# check if the user has access to this subreddit
|
||||
if not c.site.can_view(c.user):
|
||||
abort(403, "forbidden")
|
||||
|
||||
#check over 18
|
||||
if c.site.over_18 and not c.over18:
|
||||
d = dict(dest=reddit_link(request.path, url = True) + utils.query_string(request.GET))
|
||||
if c.site.over_18 and not c.over18 and not request.path == "/frame":
|
||||
d = dict(dest=add_sr(request.path) + utils.query_string(request.GET))
|
||||
return redirect_to("/over18" + utils.query_string(d))
|
||||
|
||||
#check content cache
|
||||
@@ -425,7 +440,7 @@ class RedditController(BaseController):
|
||||
def check_modified(self, thing, action):
|
||||
if c.user_is_loggedin:
|
||||
return
|
||||
|
||||
|
||||
date = utils.is_modified_since(thing, action, request.if_modified_since)
|
||||
if date is True:
|
||||
abort(304, 'not modified')
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
################################################################################
|
||||
from pylons.controllers.util import redirect_to
|
||||
from r2.lib.base import BaseController
|
||||
from pylons import c
|
||||
|
||||
class RedirectController(BaseController):
|
||||
def GET_redirect(self, dest):
|
||||
|
||||
@@ -26,7 +26,7 @@ from r2.lib import utils, captcha
|
||||
from r2.lib.filters import unkeep_space, websafe
|
||||
from r2.lib.db.operators import asc, desc
|
||||
from r2.config import cache
|
||||
from r2.lib.template_helpers import reddit_link
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.jsonresponse import json_respond
|
||||
|
||||
from r2.models import *
|
||||
@@ -77,8 +77,10 @@ def validate(*simple_vals, **param_vals):
|
||||
return fn(self, *a, **kw)
|
||||
|
||||
except UserRequiredException:
|
||||
d = dict(dest=reddit_link(request.path, url = True)
|
||||
+ utils.query_string(request.GET))
|
||||
d = dict(dest=add_sr(request.path) +
|
||||
utils.query_string(request.GET))
|
||||
if c.cname:
|
||||
d['cnameframe'] = 1
|
||||
path = "/login"
|
||||
if request.environ.get('extension'):
|
||||
path += ".%s" % request.environ['extension']
|
||||
@@ -633,19 +635,22 @@ class VReason(Validator):
|
||||
|
||||
if reason.startswith('redirect_'):
|
||||
dest = reason[9:]
|
||||
if (not dest.startswith(c.site.path) and
|
||||
not dest.startswith("http:")):
|
||||
dest = (c.site.path + dest).replace('//', '/')
|
||||
return ('redirect', dest)
|
||||
if reason.startswith('vote_'):
|
||||
fullname = reason[5:]
|
||||
t = Thing._by_fullname(fullname, data=True)
|
||||
return ('redirect', t.permalink)
|
||||
return ('redirect', t.make_permalink_slow())
|
||||
elif reason.startswith('share_'):
|
||||
fullname = reason[6:]
|
||||
t = Thing._by_fullname(fullname, data=True)
|
||||
return ('redirect', t.permalink)
|
||||
return ('redirect', t.make_permalink_slow())
|
||||
elif reason.startswith('reply_'):
|
||||
fullname = reason[6:]
|
||||
t = Thing._by_fullname(fullname, data=True)
|
||||
return ('redirect', t.permalink)
|
||||
return ('redirect', t.make_permalink_slow())
|
||||
elif reason.startswith('sr_change_'):
|
||||
sr_list = reason[10:].split(',')
|
||||
fullnames = dict(i.split(':') for i in sr_list)
|
||||
@@ -695,6 +700,17 @@ class ValidEmails(Validator):
|
||||
# return single email if one is expected, list otherwise
|
||||
return list(emails)[0] if self.num == 1 else emails
|
||||
|
||||
|
||||
class VCnameDomain(Validator):
|
||||
domain_re = re.compile(r'.+\..+')
|
||||
|
||||
def run(self, domain):
|
||||
if (domain
|
||||
and (not self.domain_re.match(domain)
|
||||
or domain.endswith('.reddit.com'))):
|
||||
c.errors.add(errors.BAD_CNAME)
|
||||
return domain or ''
|
||||
|
||||
# NOTE: make sure *never* to have res check these are present
|
||||
# otherwise, the response could contain reference to these errors...!
|
||||
class ValidIP(Validator):
|
||||
|
||||
@@ -49,7 +49,8 @@ class Globals(object):
|
||||
'uncompressedJS',
|
||||
'enable_doquery',
|
||||
'use_query_cache',
|
||||
'write_query_queue']
|
||||
'write_query_queue',
|
||||
'css_killswitch']
|
||||
|
||||
tuple_props = ['memcaches',
|
||||
'rec_cache',
|
||||
@@ -57,6 +58,7 @@ class Globals(object):
|
||||
'monitored_servers',
|
||||
'default_srs',
|
||||
'agents',
|
||||
'allowed_css_linked_domains',
|
||||
'query_caches']
|
||||
|
||||
def __init__(self, global_conf, app_conf, paths, **extra):
|
||||
@@ -157,7 +159,15 @@ class Globals(object):
|
||||
self.log.addHandler(logging.StreamHandler())
|
||||
if self.debug:
|
||||
self.log.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
#read in our CSS so that it can become a default for subreddit
|
||||
#stylesheets
|
||||
stylesheet_path = os.path.join(paths.get('static_files'),
|
||||
self.static_path.lstrip('/'),
|
||||
self.stylesheet)
|
||||
with open(stylesheet_path) as s:
|
||||
self.default_stylesheet = s.read()
|
||||
|
||||
def __del__(self):
|
||||
"""
|
||||
Put any cleanup code to be run when the application finally exits
|
||||
|
||||
@@ -94,8 +94,12 @@ class BaseController(WSGIController):
|
||||
|
||||
@staticmethod
|
||||
def redirect(dest, code = 302):
|
||||
c.response.headers['Location'] = _force_unicode(dest).encode('utf8')
|
||||
dest = _force_unicode(dest).encode('utf8')
|
||||
if c.cname and "?cnameframe=1" not in red:
|
||||
dest += "?cnameframe=1"
|
||||
c.response.headers['Location'] = dest
|
||||
c.response.status_code = code
|
||||
|
||||
return c.response
|
||||
|
||||
def sendjs(self,js, callback="document.write", escape=True):
|
||||
|
||||
359
r2/r2/lib/cssfilter.py
Normal file
359
r2/r2/lib/cssfilter.py
Normal file
@@ -0,0 +1,359 @@
|
||||
# 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-2008
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from __future__ import with_statement
|
||||
|
||||
from r2.models import *
|
||||
from r2.lib.utils import sanitize_url, domain
|
||||
from r2.lib.strings import string_dict
|
||||
|
||||
from pylons import g
|
||||
from pylons.i18n import _
|
||||
|
||||
import re
|
||||
|
||||
import cssutils
|
||||
from cssutils import CSSParser
|
||||
from cssutils.css import CSSStyleRule
|
||||
from cssutils.css import CSSValue, CSSValueList
|
||||
from cssutils.css import CSSPrimitiveValue
|
||||
from cssutils.css import cssproperties
|
||||
from xml.dom import DOMException
|
||||
|
||||
msgs = string_dict['css_validator_messages']
|
||||
|
||||
custom_macros = {
|
||||
'num': r'[-]?\d+|[-]?\d*\.\d+',
|
||||
'percentage': r'{num}%',
|
||||
'length': r'0|{num}(em|ex|px|in|cm|mm|pt|pc)',
|
||||
'color': r'orangered|dimgray|lightgray|whitesmoke|pink',
|
||||
}
|
||||
|
||||
custom_values = {
|
||||
'_height': r'{length}|{percentage}|auto|inherit',
|
||||
'_width': r'{length}|{percentage}|auto|inherit',
|
||||
'_overflow': r'visible|hidden|scroll|auto|inherit',
|
||||
'color': r'{color}',
|
||||
'background-color': r'{color}',
|
||||
'border-color': r'{color}',
|
||||
'background-position': r'(({percentage}|{length}){0,3})?\s*(top|center|left)?\s*(left|center|right)?',
|
||||
'opacity': r'{num}',
|
||||
'filter': r'alpha\(opacity={num}\)',
|
||||
}
|
||||
|
||||
def _expand_macros(tokdict,macrodict):
|
||||
""" Expand macros in token dictionary """
|
||||
def macro_value(m):
|
||||
return '(?:%s)' % macrodict[m.groupdict()['macro']]
|
||||
for key, value in tokdict.items():
|
||||
while re.search(r'{[a-z][a-z0-9-]*}', value):
|
||||
value = re.sub(r'{(?P<macro>[a-z][a-z0-9-]*)}',
|
||||
macro_value, value)
|
||||
tokdict[key] = value
|
||||
return tokdict
|
||||
def _compile_regexes(tokdict):
|
||||
""" Compile all regular expressions into callable objects """
|
||||
for key, value in tokdict.items():
|
||||
tokdict[key] = re.compile('^(?:%s)$' % value, re.I).match
|
||||
return tokdict
|
||||
_compile_regexes(_expand_macros(custom_values,custom_macros))
|
||||
|
||||
class ValidationReport(object):
|
||||
def __init__(self, original_text=''):
|
||||
self.errors = []
|
||||
self.original_text = original_text.split('\n') if original_text else ''
|
||||
|
||||
def __str__(self):
|
||||
"only for debugging"
|
||||
return "Report:\n" + '\n'.join(['\t' + str(x) for x in self.errors])
|
||||
|
||||
def append(self,error):
|
||||
if hasattr(error,'line'):
|
||||
error.offending_line = self.original_text[error.line-1]
|
||||
self.errors.append(error)
|
||||
|
||||
class ValidationError(Exception):
|
||||
def __init__(self, message, obj = None, line = None):
|
||||
self.message = message
|
||||
if obj is not None:
|
||||
self.obj = obj
|
||||
# self.offending_line is the text of the actual line that
|
||||
# caused the problem; it's set by the ValidationReport that
|
||||
# owns this ValidationError
|
||||
|
||||
if obj is not None and line is None and hasattr(self.obj,'_linetoken'):
|
||||
(_type1,_type2,self.line,_char) = obj._linetoken
|
||||
elif line is not None:
|
||||
self.line = line
|
||||
|
||||
def __cmp__(self, other):
|
||||
if hasattr(self,'line') and not hasattr(other,'line'):
|
||||
return -1
|
||||
elif hasattr(other,'line') and not hasattr(self,'line'):
|
||||
return 1
|
||||
else:
|
||||
return cmp(self.line,other.line)
|
||||
|
||||
|
||||
def __str__(self):
|
||||
"only for debugging"
|
||||
line = (("(%d)" % self.line)
|
||||
if hasattr(self,'line') else '')
|
||||
obj = str(self.obj) if hasattr(self,'obj') else ''
|
||||
return "ValidationError%s: %s (%s)" % (line, self.message, obj)
|
||||
|
||||
local_urls = re.compile(r'^/static/[a-z./-]+$')
|
||||
def valid_url(prop,value,report):
|
||||
url = value.getStringValue()
|
||||
if local_urls.match(url):
|
||||
pass
|
||||
elif domain(url) in g.allowed_css_linked_domains:
|
||||
pass
|
||||
else:
|
||||
report.append(ValidationError(msgs['broken_url']
|
||||
% dict(brokenurl = value.cssText),
|
||||
value))
|
||||
#elif sanitize_url(url) != url:
|
||||
# report.append(ValidationError(msgs['broken_url']
|
||||
# % dict(brokenurl = value.cssText),
|
||||
# value))
|
||||
|
||||
|
||||
def valid_value(prop,value,report):
|
||||
if not (value.valid and value.wellformed):
|
||||
if (value.wellformed
|
||||
and prop.name in cssproperties.cssvalues
|
||||
and cssproperties.cssvalues[prop.name](prop.value)):
|
||||
# it's actually valid. cssutils bug.
|
||||
pass
|
||||
elif (not value.valid
|
||||
and value.wellformed
|
||||
and prop.name in custom_values
|
||||
and custom_values[prop.name](prop.value)):
|
||||
# we're allowing it via our own custom validator
|
||||
value.valid = True
|
||||
|
||||
# see if this suddenly validates the entire property
|
||||
prop.valid = True
|
||||
prop.cssValue.valid = True
|
||||
if prop.cssValue.cssValueType == CSSValue.CSS_VALUE_LIST:
|
||||
for i in range(prop.cssValue.length):
|
||||
if not prop.cssValue.item(i).valid:
|
||||
prop.cssValue.valid = False
|
||||
prop.valid = False
|
||||
break
|
||||
elif not (prop.name in cssproperties.cssvalues or prop.name in custom_values):
|
||||
error = (msgs['invalid_property']
|
||||
% dict(cssprop = prop.name))
|
||||
report.append(ValidationError(error,value))
|
||||
else:
|
||||
error = (msgs['invalid_val_for_prop']
|
||||
% dict(cssvalue = value.cssText,
|
||||
cssprop = prop.name))
|
||||
report.append(ValidationError(error,value))
|
||||
|
||||
if value.primitiveType == CSSPrimitiveValue.CSS_URI:
|
||||
valid_url(prop,value,report)
|
||||
|
||||
error_message_extract_re = re.compile('.*\\[([0-9]+):[0-9]*:.*\\]$')
|
||||
only_whitespace = re.compile('^\s*$')
|
||||
def validate_css(string):
|
||||
p = CSSParser(raiseExceptions = True)
|
||||
|
||||
if not string or only_whitespace.match(string):
|
||||
return ('',ValidationReport())
|
||||
|
||||
report = ValidationReport(string)
|
||||
|
||||
# avoid a very expensive parse
|
||||
max_size_kb = 100;
|
||||
if len(string) > max_size_kb * 1024:
|
||||
report.append(ValidationError((msgs['too_big']
|
||||
% dict (max_size = max_size_kb))))
|
||||
return (string, report)
|
||||
|
||||
try:
|
||||
parsed = p.parseString(string)
|
||||
except DOMException,e:
|
||||
# yuck; xml.dom.DOMException can't give us line-information
|
||||
# directly, so we have to parse its error message string to
|
||||
# get it
|
||||
line = None
|
||||
line_match = error_message_extract_re.match(e.message)
|
||||
if line_match:
|
||||
line = line_match.group(1)
|
||||
if line:
|
||||
line = int(line)
|
||||
error_message= (msgs['syntax_error']
|
||||
% dict(syntaxerror = e.message))
|
||||
report.append(ValidationError(error_message,e,line))
|
||||
return (None,report)
|
||||
|
||||
for rule in parsed.cssRules:
|
||||
if rule.type == CSSStyleRule.IMPORT_RULE:
|
||||
report.append(ValidationError(msgs['no_imports'],rule))
|
||||
elif rule.type == CSSStyleRule.COMMENT:
|
||||
pass
|
||||
elif rule.type == CSSStyleRule.STYLE_RULE:
|
||||
style = rule.style
|
||||
for prop in style.getProperties():
|
||||
|
||||
if prop.cssValue.cssValueType == CSSValue.CSS_VALUE_LIST:
|
||||
for i in range(prop.cssValue.length):
|
||||
valid_value(prop,prop.cssValue.item(i),report)
|
||||
if not (prop.cssValue.valid and prop.cssValue.wellformed):
|
||||
report.append(ValidationError(msgs['invalid_property_list']
|
||||
% dict(proplist = prop.cssText),
|
||||
prop.cssValue))
|
||||
elif prop.cssValue.cssValueType == CSSValue.CSS_PRIMITIVE_VALUE:
|
||||
valid_value(prop,prop.cssValue,report)
|
||||
|
||||
# cssutils bug: because valid values might be marked
|
||||
# as invalid, we can't trust cssutils to properly
|
||||
# label valid properties, so we're going to rely on
|
||||
# the value validation (which will fail if the
|
||||
# property is invalid anyway). If this bug is fixed,
|
||||
# we should uncomment this 'if'
|
||||
|
||||
# a property is not valid if any of its values are
|
||||
# invalid, or if it is itself invalid. To get the
|
||||
# best-quality error messages, we only report on
|
||||
# whether the property is valid after we've checked
|
||||
# the values
|
||||
#if not (prop.valid and prop.wellformed):
|
||||
# report.append(ValidationError(_('invalid property'),prop))
|
||||
|
||||
else:
|
||||
report.append(ValidationError(msgs['unknown_rule_type']
|
||||
% dict(ruletype = rule.cssText),
|
||||
rule))
|
||||
|
||||
return parsed,report
|
||||
|
||||
def builder_wrapper(thing):
|
||||
if c.user.pref_compress and isinstance(thing, Link):
|
||||
thing.__class__ = LinkCompressed
|
||||
thing.score_fmt = Score.points
|
||||
return Wrapped(thing)
|
||||
|
||||
def find_preview_comments(sr):
|
||||
comments = Comment._query(Comment.c.sr_id == c.site._id,
|
||||
limit=25, data=True)
|
||||
comments = list(comments)
|
||||
if not comments:
|
||||
comments = Comment._query(limit=25, data=True)
|
||||
comments = list(comments)
|
||||
|
||||
return comments
|
||||
|
||||
def find_preview_links(sr):
|
||||
from r2.lib.normalized_hot import get_hot
|
||||
|
||||
# try to find a link to use, otherwise give up and return
|
||||
links = get_hot(c.site)
|
||||
if not links:
|
||||
sr = Subreddit._by_name(g.default_sr)
|
||||
if sr:
|
||||
links = get_hot(sr)
|
||||
|
||||
return links
|
||||
|
||||
def rendered_link(id, res, links, media, compress):
|
||||
from pylons.controllers.util import abort
|
||||
|
||||
try:
|
||||
render_style = c.render_style
|
||||
|
||||
c.render_style = 'html'
|
||||
|
||||
with c.user.safe_set_attr:
|
||||
c.user.pref_compress = compress
|
||||
c.user.pref_media = media
|
||||
|
||||
b = IDBuilder([l._fullname for l in links],
|
||||
num = 1, wrap = builder_wrapper)
|
||||
l = LinkListing(b, nextprev=False,
|
||||
show_nums=True).listing().render(style='html')
|
||||
res._update(id, innerHTML=l)
|
||||
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
|
||||
def rendered_comment(id, res, comments):
|
||||
try:
|
||||
render_style = c.render_style
|
||||
|
||||
c.render_style = 'html'
|
||||
|
||||
b = IDBuilder([x._fullname for x in comments],
|
||||
num = 1)
|
||||
l = LinkListing(b, nextprev=False,
|
||||
show_nums=False).listing().render(style='html')
|
||||
res._update('preview_comment', innerHTML=l)
|
||||
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
|
||||
class BadImage(Exception): pass
|
||||
|
||||
def clean_image(data,format):
|
||||
import Image
|
||||
from StringIO import StringIO
|
||||
|
||||
try:
|
||||
in_file = StringIO(data)
|
||||
out_file = StringIO()
|
||||
|
||||
im = Image.open(in_file)
|
||||
im = im.resize(im.size)
|
||||
|
||||
im.save(out_file,format)
|
||||
ret = out_file.getvalue()
|
||||
except IOError,e:
|
||||
raise BadImage(e)
|
||||
finally:
|
||||
out_file.close()
|
||||
in_file.close()
|
||||
|
||||
return ret
|
||||
|
||||
def save_header_image(sr, data):
|
||||
import tempfile
|
||||
from r2.lib import s3cp
|
||||
from md5 import md5
|
||||
|
||||
hash = md5(data).hexdigest()
|
||||
|
||||
try:
|
||||
f = tempfile.NamedTemporaryFile(suffix = '.png')
|
||||
f.write(data)
|
||||
f.flush()
|
||||
|
||||
resource = g.s3_thumb_bucket + sr._fullname + '.png'
|
||||
s3cp.send_file(f.name, resource, 'image/png', 'public-read', None, False)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
return 'http:/%s%s.png?v=%s' % (g.s3_thumb_bucket, sr._fullname, hash)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import logging
|
||||
log_format = logging.Formatter('sql: %(message)s')
|
||||
|
||||
settings = storage()
|
||||
settings.DEBUG = g.debug
|
||||
settings.DEBUG = False
|
||||
settings.DB_CREATE_TABLES = True
|
||||
settings.DB_APP_NAME = 'reddit'
|
||||
|
||||
|
||||
@@ -114,7 +114,11 @@ def safemarkdown(text):
|
||||
#wipe malicious javascript
|
||||
text = jscript_url.sub('', text)
|
||||
def href_handler(m):
|
||||
return '<a href="%s"' % m.group(1).replace('&', '&')
|
||||
x = m.group(1).replace('&', '&')
|
||||
if c.cname:
|
||||
return '<a target="_top" href="%s"' % x
|
||||
else:
|
||||
return '<a href="%s"' % x
|
||||
def code_handler(m):
|
||||
l = m.group(1)
|
||||
return '<code>%s</code>' % l.replace('&','&')
|
||||
@@ -125,7 +129,10 @@ def safemarkdown(text):
|
||||
|
||||
|
||||
def keep_space(text):
|
||||
return unsafe(websafe(text).replace(' ', ' ').replace('\n', ' ').replace('\t', '	'))
|
||||
text = websafe(text)
|
||||
for i in " \n\r\t":
|
||||
text=text.replace(i,'&#%02d;' % ord(i))
|
||||
return unsafe(text)
|
||||
|
||||
|
||||
def unkeep_space(text):
|
||||
|
||||
@@ -46,7 +46,7 @@ class JsonListingStub(object):
|
||||
class JsonResponse():
|
||||
# handled entried in the response object
|
||||
__slots__ = ['update', 'blur', 'focus', 'object', 'hide', 'show',
|
||||
'captcha', 'success']
|
||||
'captcha', 'success', 'call']
|
||||
|
||||
def __init__(self):
|
||||
self.update = []
|
||||
@@ -59,6 +59,10 @@ class JsonResponse():
|
||||
self.error = None
|
||||
self.success = None
|
||||
self.redirect = None
|
||||
self.call = []
|
||||
|
||||
def _call(self, fn):
|
||||
self.call.append(fn)
|
||||
|
||||
def _success(self):
|
||||
self.success = 1
|
||||
@@ -77,7 +81,11 @@ class JsonResponse():
|
||||
self.blur = f
|
||||
|
||||
def _redirect(self, red):
|
||||
self.redirect = red
|
||||
from pylons import c
|
||||
if c.cname and "?cnameframe=1" not in red:
|
||||
self.redirect = red + "?cnameframe=1"
|
||||
else:
|
||||
self.redirect = red
|
||||
|
||||
|
||||
def _update(self, name, **kw):
|
||||
|
||||
@@ -238,15 +238,17 @@ class NavButton(Styled):
|
||||
must also have its build() method called with the current path to
|
||||
set self.path. This step is done automatically if the button is
|
||||
passed to a NavMenu instance upon its construction."""
|
||||
def __init__(self, title, dest, sr_path = True, opt = '', aliases = [],
|
||||
style = "plain", **kw):
|
||||
def __init__(self, title, dest, sr_path = True,
|
||||
nocname=False, opt = '', aliases = [],
|
||||
target = "", style = "plain", **kw):
|
||||
|
||||
# keep original dest to check against c.location when rendering
|
||||
self.aliases = set(a.rstrip('/') for a in aliases)
|
||||
self.aliases.add(dest.rstrip('/'))
|
||||
self.dest = dest
|
||||
|
||||
Styled.__init__(self, style = style, sr_path = sr_path,
|
||||
Styled.__init__(self, style = style, sr_path = sr_path,
|
||||
nocname = nocname, target = target,
|
||||
title = title, opt = opt, **kw)
|
||||
|
||||
def build(self, base_path = ''):
|
||||
@@ -295,10 +297,10 @@ class NamedButton(NavButton):
|
||||
'dest' defaults to the 'name' as well (unless specified
|
||||
separately)."""
|
||||
|
||||
def __init__(self, name, sr_path = True, dest = None, **kw):
|
||||
def __init__(self, name, sr_path = True, nocname=False, dest = None, **kw):
|
||||
self.name = name.strip('/')
|
||||
NavButton.__init__(self, menu[self.name], name if dest is None else dest,
|
||||
sr_path = sr_path, **kw)
|
||||
sr_path = sr_path, nocname=nocname, **kw)
|
||||
|
||||
def selected_title(self):
|
||||
"""Overrides selected_title to use menu_selected dictionary"""
|
||||
|
||||
@@ -32,7 +32,8 @@ from r2.lib.captcha import get_iden
|
||||
from r2.lib.filters import spaceCompress, _force_unicode
|
||||
from r2.lib.menus import NavButton, NamedButton, NavMenu, PageNameNav, JsButton, menu
|
||||
from r2.lib.strings import plurals, rand_strings, strings
|
||||
from r2.lib.utils import title_to_url
|
||||
from r2.lib.utils import title_to_url, query_string
|
||||
from r2.lib.template_helpers import add_sr
|
||||
import sys
|
||||
|
||||
def get_captcha():
|
||||
@@ -119,15 +120,15 @@ class Reddit(Wrapped):
|
||||
|
||||
if self.submit_box:
|
||||
ps.append(SideBox(_('Submit a link'),
|
||||
c.site.path + 'submit', 'submit',
|
||||
'/submit', 'submit',
|
||||
subtitles = [_('to anything interesting: news article, blog entry, video, picture...')],
|
||||
show_cover = True))
|
||||
|
||||
if self.create_reddit_box:
|
||||
ps.append(SideBox(_('Create your own reddit'),
|
||||
ps.append(SideBox(_('Create your own reddit'),
|
||||
'/reddits/create', 'create',
|
||||
subtitles = rand_strings.get("create_reddit", 2),
|
||||
show_cover = True))
|
||||
show_cover = True, nocname=True))
|
||||
return ps
|
||||
|
||||
def render(self, *a, **kw):
|
||||
@@ -160,9 +161,11 @@ class Reddit(Wrapped):
|
||||
if c.user_is_loggedin:
|
||||
if c.user.name in g.admins:
|
||||
if c.user_is_admin:
|
||||
buttons += [NamedButton("adminoff", False)]
|
||||
buttons += [NamedButton("adminoff", False, nocname=True,
|
||||
target = "_self")]
|
||||
else:
|
||||
buttons += [NamedButton("adminon", False)]
|
||||
buttons += [NamedButton("adminon", False, nocname=True,
|
||||
target = "_self")]
|
||||
buttons += [NamedButton("prefs", False,
|
||||
css_class = "pref-lang")]
|
||||
else:
|
||||
@@ -170,12 +173,13 @@ class Reddit(Wrapped):
|
||||
buttons += [JsButton(g.lang_name.get(lang, lang),
|
||||
onclick = "return showlang();",
|
||||
css_class = "pref-lang")]
|
||||
buttons += [NamedButton("stats", False)]
|
||||
buttons += [NamedButton("help", False),
|
||||
NamedButton("blog", False)]
|
||||
buttons += [NamedButton("stats", False, nocname=True)]
|
||||
buttons += [NamedButton("help", False, nocname=True),
|
||||
NamedButton("blog", False, nocname=True)]
|
||||
|
||||
if c.user_is_loggedin:
|
||||
buttons += [NamedButton("logout", False)]
|
||||
buttons += [NamedButton("logout", False, nocname=True,
|
||||
target = "_self")]
|
||||
|
||||
return NavMenu(buttons, base_path = "/", type = "flatlist")
|
||||
|
||||
@@ -185,10 +189,10 @@ class Reddit(Wrapped):
|
||||
NamedButton("bookmarklets", False),
|
||||
NamedButton("buttons", False),
|
||||
NamedButton("widget", False),
|
||||
NamedButton("code", False),
|
||||
NamedButton("mobile", False),
|
||||
NamedButton("store", False),
|
||||
NamedButton("ad_inq", False),
|
||||
NamedButton("code", False, nocname=True),
|
||||
NamedButton("mobile", False, nocname=True),
|
||||
NamedButton("store", False, nocname=True),
|
||||
NamedButton("ad_inq", False, nocname=True),
|
||||
]
|
||||
|
||||
return NavMenu(buttons, base_path = "/", type = "flatlist")
|
||||
@@ -217,7 +221,7 @@ class Reddit(Wrapped):
|
||||
toolbar = [NavMenu(main_buttons, type='tabmenu')]
|
||||
if more_buttons:
|
||||
toolbar.append(NavMenu(more_buttons, title=menu.more, type='tabdrop'))
|
||||
if c.site != Default:
|
||||
if c.site != Default and not c.cname:
|
||||
toolbar.insert(0, PageNameNav('subreddit'))
|
||||
|
||||
return toolbar
|
||||
@@ -259,10 +263,10 @@ class SubredditInfoBar(Wrapped):
|
||||
class SideBox(Wrapped):
|
||||
"""Generic sidebox used to generate the 'submit' and 'create a reddit' boxes."""
|
||||
def __init__(self, title, link, css_class='', subtitles = [],
|
||||
show_cover = False):
|
||||
Wrapped.__init__(self, link = link,
|
||||
show_cover = False, nocname=False):
|
||||
Wrapped.__init__(self, link = link, target = '_top',
|
||||
title = title, css_class = css_class,
|
||||
subtitles = subtitles, show_cover = show_cover)
|
||||
subtitles = subtitles, show_cover = show_cover, nocname=nocname)
|
||||
|
||||
|
||||
class PrefsPage(Reddit):
|
||||
@@ -429,7 +433,7 @@ class LinkInfoPage(Reddit):
|
||||
|
||||
toolbar = [NavMenu(buttons, base_path = "", type="tabmenu")]
|
||||
|
||||
if c.site != Default:
|
||||
if c.site != Default and not c.cname:
|
||||
toolbar.insert(0, PageNameNav('subreddit'))
|
||||
|
||||
return toolbar
|
||||
@@ -463,7 +467,10 @@ class EditReddit(Reddit):
|
||||
Reddit.__init__(self, title = title, *a, **kw)
|
||||
|
||||
def build_toolbars(self):
|
||||
return [PageNameNav('subreddit')]
|
||||
if not c.cname:
|
||||
return [PageNameNav('subreddit')]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -659,6 +666,24 @@ class CreateSubreddit(Wrapped):
|
||||
def __init__(self, site = None, name = ''):
|
||||
Wrapped.__init__(self, site = site, name = name)
|
||||
|
||||
class SubredditStylesheet(Wrapped):
|
||||
"""form for editing or creating subreddit stylesheets"""
|
||||
def __init__(self, site = None,
|
||||
stylesheet_contents = ''):
|
||||
Wrapped.__init__(self, site = site,
|
||||
stylesheet_contents = stylesheet_contents)
|
||||
|
||||
class CssError(Wrapped):
|
||||
"""Rendered error returned to the stylesheet editing page via ajax"""
|
||||
def __init__(self, error):
|
||||
# error is an instance of cssutils.py:ValidationError
|
||||
Wrapped.__init__(self, error = error)
|
||||
|
||||
class UploadedImage(Wrapped):
|
||||
"The page rendered in the iframe during an upload of a header image"
|
||||
def __init__(self,status,img_src,op):
|
||||
Wrapped.__init__(self,
|
||||
status=status, img_src=img_src, op=op)
|
||||
|
||||
class Password(Wrapped):
|
||||
"""Form encountered when 'recover password' is clicked in the LoginFormWide."""
|
||||
@@ -1042,3 +1067,15 @@ class DetailsPage(LinkInfoPage):
|
||||
from admin_pages import Details
|
||||
return self.content_stack(self.link_listing, Details(link = self.link))
|
||||
|
||||
class Cnameframe(Wrapped):
|
||||
"""The frame page."""
|
||||
def __init__(self, original_path, sr_name, sr_title, sub_domain):
|
||||
Wrapped.__init__(self, original_path=original_path)
|
||||
self.title = "%s - %s" % (sr_title, sub_domain)
|
||||
port = request.environ.get('request_port')
|
||||
request.get['cnameframe'] = 1
|
||||
path = original_path + query_string(request.get)
|
||||
if port > 0:
|
||||
self.frame_target = "http://%s:%d/r/%s%s" % (c.domain, port, sr_name, path)
|
||||
else:
|
||||
self.frame_target = "http://%s/r/%s%s" % (c.domain, sr_name, path)
|
||||
|
||||
@@ -93,7 +93,19 @@ string_dict = dict(
|
||||
|
||||
searching_a_reddit = _('you\'re searching within the [%(reddit_name)s](%(reddit_link)s) reddit. '+
|
||||
'you can search within [your subscribed reddits](%(my_reddits_link)s) ' +
|
||||
'or [all reddits](%(all_reddits_link)s)')
|
||||
'or [all reddits](%(all_reddits_link)s)'),
|
||||
|
||||
css_validator_messages = dict(
|
||||
broken_url = _('"%(brokenurl)s" is not a valid URL'),
|
||||
invalid_property = _('"%(cssprop)s" is not a valid CSS property'),
|
||||
invalid_val_for_prop = _('"%(cssvalue)s" is not a valid value for CSS property "%(cssprop)s"'),
|
||||
too_big = _('too big. keep it under %(max_size)dkb'),
|
||||
syntax_error = _('syntax error: "%(syntaxerror)s"'),
|
||||
no_imports = _('@imports are not allowed'),
|
||||
invalid_property_list = _('invalid CSS property list "%(proplist)s"'),
|
||||
unknown_rule_type = _('unknown CSS rule type "%(ruletype)s"')
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
class StringHandler(object):
|
||||
|
||||
@@ -53,7 +53,6 @@ def static(file):
|
||||
return file + v
|
||||
return os.path.join(c.site.static_path, file) + v
|
||||
|
||||
|
||||
def generateurl(context, path, **kw):
|
||||
if kw:
|
||||
return path + '?' + '&'.join(["%s=%s"%(k, url_escape(v)) \
|
||||
@@ -145,45 +144,56 @@ def replace_render(listing, item, style = None, display = True):
|
||||
rendered_item = replace_fn(u"$ListClass", listing._js_cls)
|
||||
|
||||
#$votehash is only present when voting arrows are present
|
||||
if u'$votehash' in rendered_item:
|
||||
if c.user_is_loggedin and u'$votehash' in rendered_item:
|
||||
hash = vote_hash(c.user, item, listing.vote_hash_type)
|
||||
rendered_item = replace_fn(u'$votehash', hash)
|
||||
|
||||
rendered_item = replace_fn(u"$display", "" if display else "style='display:none'")
|
||||
return rendered_item
|
||||
|
||||
from pylons import c as cur
|
||||
def dockletStr(context, type, browser):
|
||||
domain = c.domain
|
||||
if c.cname:
|
||||
domain = c.site.domain
|
||||
|
||||
if type == "serendipity!":
|
||||
return "http://"+cur.domain+"/random"
|
||||
return "http://"+domain+"/random"
|
||||
elif type == "reddit":
|
||||
return "javascript:location.href='http://"+cur.domain+"/submit?url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)"
|
||||
return "javascript:location.href='http://"+domain+"/submit?url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)"
|
||||
else:
|
||||
f = "fixed"
|
||||
if browser == "ie": f = "absolute"
|
||||
return "javascript:function b(){var u=encodeURIComponent(location.href);var i=document.getElementById('redstat')||document.createElement('a');var s=i.style;s.position='%s';s.top='0';s.left='0';s.zIndex='10002';i.id='redstat';i.href='http://%s/submit?url='+u+'&title='+encodeURIComponent(document.title);var q=i.firstChild||document.createElement('img');q.src='http://%s/d/%s'+Math.random()+'?uh=%s&u='+u;i.appendChild(q);document.body.appendChild(i)};b()" % \
|
||||
(f, cur.domain, cur.domain, type,
|
||||
c.modhash if cur.user else '')
|
||||
(f, domain, domain, type,
|
||||
c.modhash if c.user else '')
|
||||
|
||||
|
||||
|
||||
def reddit_link(path, url = False, get = False):
|
||||
if url or get:
|
||||
(scheme, netloc, path, params, query, fragment) = urlparse(path)
|
||||
if url:
|
||||
#noslash fixes /reddits/
|
||||
noslash = c.site.path.rstrip('/')
|
||||
#if it's a relative path, don't include the sitename
|
||||
if path.startswith('/') and not path.startswith(noslash):
|
||||
def add_sr(path, sr_path = True, nocname=False):
|
||||
"""Given a link, returns that link with the subreddit added.
|
||||
Also adds the domain for cname requests."""
|
||||
(scheme, netloc, path, params, query, fragment) = urlparse(path)
|
||||
if sr_path:
|
||||
#noslash fixes /reddits/
|
||||
noslash = c.site.path.rstrip('/')
|
||||
#if it's a relative path, don't include the sitename
|
||||
if (path.startswith('/') and not path.startswith(noslash)
|
||||
and not path.startswith('/r/')):
|
||||
if not c.cname:
|
||||
path = c.site.path + path[1:]
|
||||
else:
|
||||
newparam = "r=" + url_escape(c.site.name)
|
||||
if query:
|
||||
query += "&" + newparam
|
||||
else:
|
||||
query = newparam
|
||||
return urlunparse((scheme, netloc, path, params, query, fragment))
|
||||
return path
|
||||
|
||||
if not netloc and c.cname and not nocname:
|
||||
netloc = getattr(c.site, 'domain', None)
|
||||
|
||||
if netloc:
|
||||
port = request.environ.get('request_port')
|
||||
if port > 0:
|
||||
netloc = "%s:%d" % (netloc, port)
|
||||
|
||||
if c.render_style == 'mobile' and not path.endswith('.mobile'):
|
||||
path += '.mobile'
|
||||
|
||||
return urlunparse((scheme, netloc, path, params, query, fragment))
|
||||
|
||||
def join_urls(*urls):
|
||||
"""joins a series of urls together without doubles slashes"""
|
||||
|
||||
@@ -11,7 +11,7 @@ def is_modified_since(thing, action, date):
|
||||
which means a 304 should be returned. Otherwise returns the date
|
||||
that should be sent as the last-modified header."""
|
||||
from pylons import g
|
||||
|
||||
|
||||
prop = 'last_' + action
|
||||
if not hasattr(thing, prop):
|
||||
last_modified = make_last_modified()
|
||||
|
||||
@@ -51,6 +51,7 @@ class Account(Thing):
|
||||
pref_over_18 = False,
|
||||
pref_compress = False,
|
||||
pref_organic = True,
|
||||
pref_show_stylesheets = True,
|
||||
reported = 0,
|
||||
report_made = 0,
|
||||
report_correct = 0,
|
||||
|
||||
@@ -79,7 +79,7 @@ class Link(Thing, Printable):
|
||||
|
||||
@property
|
||||
def already_submitted_link(self):
|
||||
return self.permalink + '?already_submitted=true'
|
||||
return self.make_permalink_slow() + '?already_submitted=true'
|
||||
|
||||
def resubmit_link(self, sr_url = False):
|
||||
submit_url = self.subreddit_slow.path if sr_url else '/'
|
||||
@@ -193,7 +193,9 @@ class Link(Thing, Printable):
|
||||
c.user.pref_newwindow,
|
||||
c.user.pref_frame,
|
||||
c.user.pref_compress,
|
||||
c.user.pref_media,
|
||||
request.host,
|
||||
c.cname,
|
||||
wrapped.author == c.user,
|
||||
wrapped.likes,
|
||||
wrapped.saved,
|
||||
@@ -208,10 +210,19 @@ class Link(Thing, Printable):
|
||||
s = ''.join(s)
|
||||
return s
|
||||
|
||||
@property
|
||||
def permalink(self):
|
||||
return "/comments/%s/%s/" % (self._id36, title_to_url(self.title))
|
||||
def make_permalink(self, sr):
|
||||
p = "comments/%s/%s/" % (self._id36, title_to_url(self.title))
|
||||
if not c.cname:
|
||||
res = "/r/%s/%s" % (sr.name, p)
|
||||
elif sr != c.site:
|
||||
res = "http://%s/r/%s/%s" % (g.domain, sr.name, p)
|
||||
else:
|
||||
res = "/%s" % p
|
||||
return res
|
||||
|
||||
def make_permalink_slow(self):
|
||||
return self.make_permalink(self.subreddit_slow)
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
from r2.lib.count import incr_counts
|
||||
@@ -247,6 +258,7 @@ class Link(Thing, Printable):
|
||||
item.clicked = bool(clicked.get((user, item, 'click')))
|
||||
item.num = None
|
||||
item.score_fmt = Score.number_only
|
||||
item.permalink = item.make_permalink(item.subreddit)
|
||||
|
||||
if c.user_is_loggedin:
|
||||
incr_counts(wrapped)
|
||||
@@ -333,6 +345,7 @@ class Comment(Thing, Printable):
|
||||
bool(c.user_is_loggedin),
|
||||
c.focal_comment == self._id36,
|
||||
request.host,
|
||||
c.cname,
|
||||
wrapped.author == c.user,
|
||||
wrapped.likes,
|
||||
wrapped.friend,
|
||||
@@ -347,22 +360,18 @@ class Comment(Thing, Printable):
|
||||
s = ''.join(s)
|
||||
return s
|
||||
|
||||
@property
|
||||
def permalink(self):
|
||||
if not self._loaded:
|
||||
self._load()
|
||||
|
||||
try:
|
||||
l = Link._byID(self.link_id, True)
|
||||
return l.permalink + self._id36
|
||||
except NotFound:
|
||||
return ""
|
||||
|
||||
def make_permalink(self, link, sr=None):
|
||||
return link.make_permalink(sr) + self._id36
|
||||
|
||||
def make_permalink_slow(self):
|
||||
l = Link._byID(self.link_id, data=True)
|
||||
return self.make_permalink(l, l.subreddit_slow)
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
#fetch parent links
|
||||
links = Link._byID(set(l.link_id for l in wrapped), True)
|
||||
|
||||
|
||||
#get srs for comments that don't have them (old comments)
|
||||
for cm in wrapped:
|
||||
@@ -378,19 +387,20 @@ class Comment(Thing, Printable):
|
||||
cids = dict((w._id, w) for w in wrapped)
|
||||
|
||||
for item in wrapped:
|
||||
item.link = links.get(item.link_id)
|
||||
if not hasattr(item, 'subreddit'):
|
||||
item.subreddit = item.subreddit_slow
|
||||
if hasattr(item, 'parent_id'):
|
||||
if cids.has_key(item.parent_id):
|
||||
item.parent_permalink = '#' + utils.to36(item.parent_id)
|
||||
else:
|
||||
parent = Comment._byID(item.parent_id)
|
||||
item.parent_permalink = parent.permalink
|
||||
item.parent_permalink = parent.make_permalink(item.link, item.subreddit)
|
||||
else:
|
||||
item.parent_permalink = None
|
||||
|
||||
item.can_reply = (item.sr_id in can_reply_srs)
|
||||
|
||||
if not hasattr(item, 'subreddit'):
|
||||
item.subreddit = item.subreddit_slow
|
||||
|
||||
# not deleted on profile pages,
|
||||
# deleted if spam and not author or admin
|
||||
@@ -406,12 +416,12 @@ class Comment(Thing, Printable):
|
||||
item.deleted or
|
||||
c.user_is_admin))
|
||||
|
||||
item.link = links.get(item.link_id)
|
||||
if not hasattr(item,'editted'):
|
||||
item.editted = False
|
||||
#will get updated in builder
|
||||
item.num_children = 0
|
||||
item.score_fmt = Score.points
|
||||
item.permalink = item.make_permalink(item.link, item.subreddit)
|
||||
|
||||
class MoreComments(object):
|
||||
show_spam = False
|
||||
@@ -432,7 +442,7 @@ class MoreComments(object):
|
||||
if parent:
|
||||
self.parent_id = parent._id
|
||||
self.parent_name = parent._fullname
|
||||
self.parent_permalink = parent.permalink
|
||||
self.parent_permalink = parent.make_permalink(link)
|
||||
self.link_name = link._fullname
|
||||
self.link_id = link._id
|
||||
self.depth = depth
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
################################################################################
|
||||
from r2.models import *
|
||||
|
||||
def populate():
|
||||
sr = Subreddit._new(name= 'reddit.com', title = "reddit.com: what's new online")
|
||||
def populate(sr_name = 'reddit.com', start_account = 1, sr_title = "reddit.com: what's new online"):
|
||||
sr = Subreddit._new(name= sr_name, title = sr_title)
|
||||
sr._commit()
|
||||
for i in range(1,5):
|
||||
for i in range(start_account,(start_account + 4)):
|
||||
name = 'test' + str(i)
|
||||
password = name
|
||||
user = register(name, password)
|
||||
|
||||
@@ -30,6 +30,7 @@ from r2.lib.db.operators import lower, or_, and_, desc
|
||||
from r2.lib.memoize import memoize, clear_memo
|
||||
from r2.lib.utils import tup
|
||||
from r2.lib.strings import strings, Score
|
||||
from r2.lib.filters import _force_unicode
|
||||
|
||||
import os.path
|
||||
|
||||
@@ -37,6 +38,8 @@ class Subreddit(Thing, Printable):
|
||||
_defaults = dict(static_path = g.static_path,
|
||||
stylesheet = None,
|
||||
stylesheet_rtl = None,
|
||||
stylesheet_contents = '',
|
||||
stylesheet_hash = '0',
|
||||
description = None,
|
||||
firsttext = strings.firsttext,
|
||||
header = os.path.join(g.static_path,
|
||||
@@ -45,6 +48,7 @@ class Subreddit(Thing, Printable):
|
||||
reported = 0,
|
||||
valid_votes = 0,
|
||||
show_media = False,
|
||||
domain = None,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -91,6 +95,24 @@ class Subreddit(Thing, Printable):
|
||||
else:
|
||||
raise NotFound, 'Subreddit %s' % name
|
||||
|
||||
@classmethod
|
||||
@memoize('subreddit._by_domain')
|
||||
def _by_domain_cache(cls, name):
|
||||
q = cls._query(cls.c.domain == name,
|
||||
cls.c.over_18 == (True, False),
|
||||
limit = 1)
|
||||
l = list(q)
|
||||
if l:
|
||||
return l[0]._id
|
||||
|
||||
@classmethod
|
||||
def _by_domain(cls, domain):
|
||||
sr_id = cls._by_domain_cache(_force_unicode(domain))
|
||||
if sr_id:
|
||||
return cls._byID(sr_id, True)
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def moderators(self):
|
||||
return self.moderator_ids()
|
||||
@@ -138,6 +160,12 @@ class Subreddit(Thing, Printable):
|
||||
and (c.user_is_admin
|
||||
or self.is_moderator(user)))
|
||||
|
||||
def can_change_stylesheet(self, user):
|
||||
if c.user_is_loggedin:
|
||||
return c.user_is_admin or self.is_moderator(user)
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_special(self, user):
|
||||
return (user
|
||||
and (c.user_is_admin
|
||||
@@ -341,6 +369,9 @@ class FakeSubreddit(Subreddit):
|
||||
def can_submit(self, user):
|
||||
return False
|
||||
|
||||
def can_change_stylesheet(self, user):
|
||||
return False
|
||||
|
||||
def is_banned(self, user):
|
||||
return False
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ document.write('<script language="JavaScript" src="http://ad.doubleclick.net/adj
|
||||
</div>
|
||||
|
||||
<div id="adlink">
|
||||
<a target="_parent" href="#" onclick="rwt(this)">reddit this ad</a>
|
||||
<a target="_top" href="#" onclick="rwt(this)">reddit this ad</a>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td,iframe{ margin:0;padding:0;}
|
||||
table{ border-collapse:collapse;}
|
||||
fieldset,img{ border:0;}
|
||||
address,caption,cite,code,dfn,em,strong,th,var{ font-style:normal;font-weight:normal;}
|
||||
ol,ul { list-style:none;}
|
||||
caption,th { text-align:left;}
|
||||
h1,h2,h3,h4,h5,h6{ font-size:100%;}
|
||||
q:before,q:after{ content:'';}
|
||||
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,p,blockquote,th,td,iframe {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
|
||||
table { border-collapse:collapse; }
|
||||
|
||||
fieldset,img { border:0; }
|
||||
|
||||
address,caption,cite,code,dfn,em,strong,th,var { font-style:normal; font-weight:normal; }
|
||||
ol,ul { list-style:none; }
|
||||
caption,th { text-align:left; }
|
||||
h1,h2,h3,h4,h5,h6 { font-size:100%; }
|
||||
q:before,q:after { content:''; }
|
||||
|
||||
body {
|
||||
font: normal x-small verdana, arial, helvetica, sans-serif;
|
||||
@@ -13,8 +19,12 @@ body {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
html,body { height: 100% }
|
||||
html,body { height: 100%; }
|
||||
|
||||
/* IE dumbness patch. hidden input in a hidden block that is
|
||||
subsequently shown leads to the input to "show" and generate undesired
|
||||
padding. This makes it go away. */
|
||||
input[type=hidden] { position: absolute; }
|
||||
|
||||
/* html element defaults */
|
||||
|
||||
@@ -85,10 +95,7 @@ input.txt {
|
||||
background-color: #cee3f8;
|
||||
}
|
||||
|
||||
#header-img {
|
||||
height: 40px;
|
||||
width: 120px;
|
||||
}
|
||||
#header-img {}
|
||||
|
||||
#header-top {
|
||||
position: absolute;
|
||||
@@ -460,25 +467,14 @@ before enabling */
|
||||
height: 14px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-position: center 0px;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.arrow.upmod {
|
||||
background: url(/static/aupmod.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 0px; }
|
||||
.arrow.downmod {
|
||||
background: url(/static/adownmod.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 0px; }
|
||||
.arrow.up {
|
||||
background: url(/static/aupgray.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 0px; }
|
||||
.arrow.down {
|
||||
background: url(/static/adowngray.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: center 0px;}
|
||||
.arrow.upmod { background-image: url(/static/aupmod.png); }
|
||||
.arrow.downmod { background-image: url(/static/adownmod.png); }
|
||||
.arrow.up { background-image: url(/static/aupgray.png); }
|
||||
.arrow.down { background-image: url(/static/adowngray.png); }
|
||||
|
||||
.midcol {
|
||||
float: left;
|
||||
@@ -1196,7 +1192,12 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
#preview { float: right; width: 20em; margin-right: 10px; }
|
||||
#preview span { color: lightgray; }
|
||||
#preview #previewbox { border: .2em dashed lightgray; padding: 1em; }
|
||||
#preview #previewbox {
|
||||
border-width: .2em;
|
||||
border-style: dashed;
|
||||
border-color: lightgray;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.pretty-form {
|
||||
font-size: larger;
|
||||
@@ -1204,6 +1205,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
}
|
||||
|
||||
.pretty-form input[type=checkbox] {margin: 3px .5em;}
|
||||
.pretty-form p {margin: 3px .5em;}
|
||||
.pretty-form input[type=radio] {margin: 3px .5em 0px .5em; vertical-align:top}
|
||||
.pretty-form img { margin: 3px .5em}
|
||||
.pretty-form input[type=text],
|
||||
@@ -1280,3 +1282,63 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* CSS customisation page */
|
||||
|
||||
.stylesheet-customize-container { }
|
||||
.stylesheet-customize-container textarea { margin: 0; padding: 0px; }
|
||||
|
||||
.sheets { margin-right: 315px; }
|
||||
.sheets .col { float: left; }
|
||||
.sheets .col > div { margin: 0 5px; }
|
||||
.sheets .col textarea { width: 100% }
|
||||
.sheets .buttons { margin-left: 5px }
|
||||
.sheets .btn { margin-left: 0px; margin-right: 5px; }
|
||||
.sheets .btn.right { float: right; margin-right: 3px;}
|
||||
|
||||
.stylesheet-customize-container h2 { margin-top: 15px; margin-bottom: 10px; }
|
||||
|
||||
#validation-errors {
|
||||
margin-left: 40px;
|
||||
margin-top: 10px;
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
#validation-errors a,
|
||||
#validation-errors li,
|
||||
.errors h2 { color: red }
|
||||
|
||||
#validation-errors a:hover { text-decoration: underline; }
|
||||
#validation-errors pre { padding: 10px; color: black; }
|
||||
|
||||
#preview-table {
|
||||
padding-right: 15px;
|
||||
}
|
||||
#preview-table > table {
|
||||
border-width: .2em;
|
||||
border-style: dashed;
|
||||
border-color: lightgray;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#preview-table > table > tbody > tr { padding-bottom: 10px; }
|
||||
#preview-table > table > tbody > tr > td { padding: 5px; padding-right: 15px;}
|
||||
#preview-table > table > tbody > tr > th { padding: 5px; padding-right: 15px;}
|
||||
#preview-table > table > tbody > tr > th {
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
font-size: larger;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#header-img-preview-container {
|
||||
border-width: .2em;
|
||||
border-style: dashed;
|
||||
border-color: lightgray;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
|
||||
BIN
r2/r2/public/static/transpLOGO.png
Normal file
BIN
r2/r2/public/static/transpLOGO.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
@@ -112,6 +112,9 @@ function redditRequest(op, parameters, worker_in, block) {
|
||||
if (post_site) {
|
||||
parameters.r = post_site;
|
||||
}
|
||||
if (cnameframe) {
|
||||
parameters.cnameframe = 1;
|
||||
}
|
||||
op = api_loc + op;
|
||||
if(!worker) {
|
||||
worker = handleResponse(action);
|
||||
@@ -161,6 +164,183 @@ function tup(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
function stylesheetSave(form, formID) {
|
||||
form.op.value = "save";
|
||||
return post_form(form, formID);
|
||||
}
|
||||
|
||||
function stylesheetPreview(formID, textboxID) {
|
||||
var form = document.getElementById(formID);
|
||||
form.op.value = "preview";
|
||||
post_form(form, formID);
|
||||
|
||||
applyStylesheetFromTextbox(textboxID);
|
||||
}
|
||||
|
||||
function applyStylesheetFromTextbox(textboxID) {
|
||||
var textbox = document.getElementById(textboxID);
|
||||
var cssText = textbox.value;
|
||||
return applyStylesheet(cssText);
|
||||
}
|
||||
|
||||
function applyStylesheet(cssText) {
|
||||
/* also referred to in the reddit.html template, for the name of the
|
||||
stylesheet set for this reddit. These must be in sync, because
|
||||
I'm over-writing it here */
|
||||
var sheet_title = 'applied_subreddit_stylesheet';
|
||||
|
||||
if(document.styleSheets[0].cssText) {
|
||||
/* of course IE has to do this differently from everyone else. */
|
||||
for(var x=0; x < document.styleSheets.length; x++) {
|
||||
if(document.styleSheets[x].title == sheet_title) {
|
||||
document.styleSheets[x].cssText = cssText;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* for everyone else, we walk <head> for the <link> or <style>
|
||||
that has the old stylesheet, and delete it. Then we add a
|
||||
<style> with the new one */
|
||||
var headNode = document.getElementsByTagName("head")[0];
|
||||
var headNodes = headNode.childNodes;
|
||||
|
||||
for(var x=0; x < headNodes.length; x++) {
|
||||
var node = headNodes[x];
|
||||
|
||||
if(node.title == sheet_title) {
|
||||
headNode.removeChild(node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var appliedCSSNode = document.createElement('style');
|
||||
appliedCSSNode.type = 'text/css';
|
||||
appliedCSSNode.rel = 'stylesheet';
|
||||
appliedCSSNode.media = 'screen';
|
||||
appliedCSSNode.title = sheet_title;
|
||||
|
||||
appliedCSSNode.textContent = cssText;
|
||||
|
||||
headNode.appendChild(appliedCSSNode);
|
||||
}
|
||||
}
|
||||
|
||||
function showDefaultStylesheet() {
|
||||
return toggleDefaultStylesheet(true);
|
||||
}
|
||||
function hideDefaultStylesheet() {
|
||||
return toggleDefaultStylesheet(false);
|
||||
}
|
||||
function toggleDefaultStylesheet(p_show) {
|
||||
var stylesheet_contents = $('stylesheet_contents').parentNode.parentNode;
|
||||
var default_stylesheet = $('default_stylesheet').parentNode.parentNode;
|
||||
|
||||
var show_button = $('show_default_stylesheet');
|
||||
var hide_button = $('hide_default_stylesheet');
|
||||
|
||||
if(p_show) {
|
||||
default_stylesheet.style.width = "50%";
|
||||
stylesheet_contents.style.width = "50%";
|
||||
show(default_stylesheet, hide_button);
|
||||
hide(show_button);
|
||||
} else {
|
||||
stylesheet_contents.style.width = "100%";
|
||||
default_stylesheet.style.width = "";
|
||||
show(show_button);
|
||||
hide(default_stylesheet, hide_button);
|
||||
}
|
||||
|
||||
return false; // don't post the form
|
||||
}
|
||||
|
||||
function gotoTextboxLine(textboxID, lineNo) {
|
||||
var textbox = $(textboxID);
|
||||
var text = textbox.value;
|
||||
|
||||
var newline = '\n';
|
||||
var newline_length = 1;
|
||||
var caret_pos = 0;
|
||||
|
||||
if ( text.indexOf('\r') > 0) {
|
||||
/* IE hack */
|
||||
newline = '\r';
|
||||
newline_length = 0;
|
||||
caret_pos = 1;
|
||||
}
|
||||
|
||||
var lines = textbox.value.split(newline);
|
||||
|
||||
for(var x=0; x<lineNo-1; x++) {
|
||||
caret_pos += lines[x].length + newline_length;
|
||||
}
|
||||
var end_pos = caret_pos;
|
||||
if (lineNo < lines.length) {
|
||||
end_pos += lines[lineNo-1].length + newline_length;
|
||||
}
|
||||
|
||||
|
||||
textbox.focus();
|
||||
if(textbox.createTextRange) { /* IE */
|
||||
var start = textbox.createTextRange();
|
||||
start.move('character', caret_pos);
|
||||
var end = textbox.createTextRange();
|
||||
end.move('character', end_pos);
|
||||
start.setEndPoint("StartToEnd", end);
|
||||
start.select();
|
||||
} else if (textbox.selectionStart) {
|
||||
textbox.setSelectionRange(caret_pos, end_pos);
|
||||
}
|
||||
|
||||
if(textbox.scrollHeight) {
|
||||
var avgLineHight = textbox.scrollHeight / lines.length;
|
||||
textbox.scrollTop = (lineNo-2) * avgLineHight;
|
||||
}
|
||||
}
|
||||
|
||||
function uploadHeaderImage(status) {
|
||||
var form = $('upload-header-image');
|
||||
var iframe = $('upload-header-iframe');
|
||||
|
||||
form.op.value = 'upload';
|
||||
$('img-status').innerHTML = status;
|
||||
show('img-status');
|
||||
|
||||
form.submit();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function deleteHeaderImage(status) {
|
||||
var form = $('upload-header-image');
|
||||
var iframe = $('upload-header-iframe');
|
||||
|
||||
form.reset();
|
||||
form.op.value = 'delete';
|
||||
$('img-status').innerHTML = status;
|
||||
show('img-status');
|
||||
|
||||
form.submit();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function completedUploadHeaderImage(status,img_src,op) {
|
||||
show('img-status');
|
||||
$('img-status').innerHTML = status;
|
||||
$('upload-header-image').reset();
|
||||
|
||||
$('header-img').src = img_src;
|
||||
|
||||
if(op == 'delete') {
|
||||
hide('delete-header-img');
|
||||
hide('header-img-preview-container');
|
||||
} else {
|
||||
$('header-img-preview').src = img_src;
|
||||
show('delete-header-img');
|
||||
show('header-img-preview-container');
|
||||
}
|
||||
}
|
||||
|
||||
function handleResponse(action) {
|
||||
var my_iter = function(x, func) {
|
||||
if(x) {
|
||||
@@ -209,6 +389,13 @@ function handleResponse(action) {
|
||||
}
|
||||
});
|
||||
}
|
||||
// handle applied CSS
|
||||
if(r.call) {
|
||||
var calls = r.call;
|
||||
for(var i=0; i<calls.length; i++) {
|
||||
eval(calls[i]);
|
||||
}
|
||||
}
|
||||
// handle shifts of focus
|
||||
if (r.focus) {
|
||||
var f = $(r.focus);
|
||||
@@ -387,4 +574,4 @@ function view_embeded_media(id, media_link) {
|
||||
closespan.style.display = "none";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,10 @@ ${self.robots()}
|
||||
|
||||
var logged = ${c.user_is_loggedin and ("'%s'" % c.user.name) or "false"};
|
||||
var post_site = "${c.site.name}";
|
||||
var cnameframe = ${'true' if c.cname else 'false'};
|
||||
var modhash = ${"'%s'" % c.modhash or "false"};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
${self.javascript()}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution
|
||||
## 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
|
||||
|
||||
26
r2/r2/templates/cnameframe.html
Normal file
26
r2/r2/templates/cnameframe.html
Normal file
@@ -0,0 +1,26 @@
|
||||
<%!
|
||||
from r2.lib.template_helpers import join_urls
|
||||
%>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="${c.lang}"
|
||||
xml:lang="${c.lang}" ${c.lang_rtl and unsafe('dir="rtl"') or ''}>
|
||||
<head>
|
||||
<title>${thing.title}</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS"
|
||||
href="${join_urls(thing.original_path,'.rss')}" />
|
||||
|
||||
</head>
|
||||
|
||||
<frameset>
|
||||
<frame src="${thing.frame_target}">
|
||||
</frameset>
|
||||
|
||||
<body>
|
||||
|
||||
<p class="error">Your browser does not support frames. Go <a href="${thing.frame_target}">here</a>.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution
|
||||
## 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
|
||||
@@ -17,13 +17,13 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.filters import keep_space
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field, language_tool"/>
|
||||
<%namespace file="utils.html" import="error_field, language_tool, plain_link"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
function update_title() {
|
||||
@@ -113,6 +113,7 @@ function update_title() {
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label for="description">${_("description")}</label>
|
||||
@@ -129,6 +130,7 @@ function update_title() {
|
||||
${error_field("DESC_TOO_LONG", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
%if c.user_is_admin:
|
||||
<tr>
|
||||
<th>
|
||||
@@ -248,15 +250,36 @@ function update_title() {
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>
|
||||
<label for="domain">${_("domain")}</label>
|
||||
</th>
|
||||
<td>
|
||||
%if thing.site:
|
||||
<input id="domain" type="text" name="domain" class="text"
|
||||
value="${getattr(thing.site, 'domain', None) or ""}"/>
|
||||
%else:
|
||||
<input id="domain" type="text" name="domain" class="text" />
|
||||
%endif
|
||||
</td>
|
||||
<td id="note_domain">
|
||||
${error_field("BAD_CNAME", "span")}
|
||||
${error_field("USED_CNAME", "span")}
|
||||
%if not thing.site:
|
||||
<span class="gray" id="example_domain">
|
||||
${_("e.g. reddit.slashdot.org (optional)")}
|
||||
</span>
|
||||
%endif
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
</th>
|
||||
<td colspan="2">
|
||||
%if thing.site:
|
||||
<button name="edit" type="submit">${_("update")}</button>
|
||||
<button name="edit" class="btn" type="submit">${_("update")}</button>
|
||||
%else:
|
||||
<button name="create" type="submit">${_("create")}</button>
|
||||
<button name="create" class="btn" type="submit">${_("create")}</button>
|
||||
%endif
|
||||
 
|
||||
<span id="status" class="error"></span>
|
||||
@@ -266,3 +289,64 @@ function update_title() {
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
%if thing.site and thing.site.can_change_stylesheet(c.user) and not g.css_killswitch:
|
||||
<h1>${_('change the look')}</h1>
|
||||
|
||||
<table class="content preftable pretty-form sr-form">
|
||||
<tr>
|
||||
<th>
|
||||
<label for="stylesheet_contents">${_("edit stylesheet")}</label>
|
||||
</th>
|
||||
<td class="nowrap nopadding">
|
||||
<p>${plain_link(_('edit the stylesheet'),
|
||||
'/about/stylesheet',
|
||||
sr_path = True)}</p>
|
||||
</td>
|
||||
<td id="note_stylesheet_contents"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><label for="file">${_('upload header image')}</label></th>
|
||||
<td>
|
||||
<form
|
||||
onsubmit="return uploadHeaderImage('${_('submitting')}');"
|
||||
name="upload-header-image" id="upload-header-image"
|
||||
enctype="multipart/form-data"
|
||||
class="pretty-form sr-form"
|
||||
target="upload-header-iframe"
|
||||
action="/api/upload_header_img" method="post">
|
||||
<input type="file" name="file"
|
||||
onchange="return uploadHeaderImage('${_('submitting')}')" />
|
||||
<button id="delete-header-img"
|
||||
class="btn"
|
||||
onclick="return deleteHeaderImage('${_('submitting')}')"
|
||||
%if not thing.site.header:
|
||||
style="display: none;"
|
||||
%endif
|
||||
>
|
||||
${_('restore default')}
|
||||
</button>
|
||||
<input type="hidden" name="uh" value="${c.modhash}" />
|
||||
<input type="hidden" name="r" value="${c.site.name}" />
|
||||
<input type="hidden" name="op" value="upload" />
|
||||
|
||||
<span style="display: none;" class="error" id="img-status"></span>
|
||||
<iframe src="about:blank"
|
||||
width="600" height="200" style="display: none;"
|
||||
name="upload-header-iframe" id="upload-header-iframe"></iframe>
|
||||
</form>
|
||||
<div id="header-img-preview-container" style="display: none;">
|
||||
<img id="header-img-preview"
|
||||
%if thing.site.header:
|
||||
src="${thing.site.header}"
|
||||
%else:
|
||||
src="/static/kill.png"
|
||||
%endif
|
||||
/ ><br />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
%endif
|
||||
|
||||
|
||||
43
r2/r2/templates/csserror.html
Normal file
43
r2/r2/templates/csserror.html
Normal file
@@ -0,0 +1,43 @@
|
||||
## 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-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<% error = thing.error %>
|
||||
<li>
|
||||
%if hasattr(error,'line'):
|
||||
<a href="javascript:gotoTextboxLine('stylesheet_contents',${error.line})">
|
||||
[line ${error.line}]
|
||||
%endif
|
||||
|
||||
${error.message}
|
||||
|
||||
%if hasattr(error,'offending_line'):
|
||||
<div>
|
||||
<tt>
|
||||
<pre>${error.offending_line}</pre>
|
||||
</tt>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if hasattr(error,'line'):
|
||||
</a>
|
||||
%endif
|
||||
|
||||
</li>
|
||||
@@ -56,6 +56,9 @@
|
||||
%if c.user.pref_newwindow:
|
||||
target="_blank"
|
||||
%endif
|
||||
%if c.cname:
|
||||
target="_top"
|
||||
%endif
|
||||
>
|
||||
${caller.body()}
|
||||
</a>
|
||||
@@ -142,7 +145,7 @@
|
||||
com_label = _("comment {verb}")
|
||||
else:
|
||||
# generates "XX comments" as a noun
|
||||
com_label = ungettext("comment", "comments", thing.num_comments)
|
||||
com_label = ungettext("comment", "comments", thing.num_comments)
|
||||
%>
|
||||
<li class="first">
|
||||
${parent.comment_button("comment", fullname, com_label,
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
from pylons.i18n import _, ungettext
|
||||
%>
|
||||
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
<%inherit file="printable.mobile" />
|
||||
|
||||
<%def name="entry()">
|
||||
@@ -39,7 +40,8 @@
|
||||
com_label = _("comment")
|
||||
%>
|
||||
<p class="link"><a href="${thing.url}" class="title">${thing.title}</a></p>
|
||||
<p class="byline">${thing.score} ${ungettext("point", "points", thing.score)} | <span class="buttons"><a href="${thing.permalink}">${com_label}</a></span> | ${tagline()}</p>
|
||||
<p class="byline">${thing.score} ${ungettext("point", "points", thing.score)} | <span class="buttons">
|
||||
${plain_link(com_label, thing.permalink)}</span> | ${tagline()}</p>
|
||||
</%def>
|
||||
|
||||
<%def name="domain()" buffered="True">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<%!
|
||||
from pylons.i18n import _, ungettext
|
||||
from r2.lib.template_helpers import reddit_link
|
||||
from r2.lib.template_helpers import add_sr
|
||||
%>
|
||||
|
||||
<item>
|
||||
@@ -32,7 +32,7 @@
|
||||
<dc:date>${thing._date.isoformat()}</dc:date>
|
||||
<description>
|
||||
<a href="${thing.url}">[link]</a>
|
||||
<a href="http://${request.host}${reddit_link(thing.permalink, url=True)}">[comments]</a>
|
||||
<a href="http://${request.host}${add_sr(thing.permalink, nocname=True)}">[comments]</a>
|
||||
</description>
|
||||
</item>
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<% from r2.lib.template_helpers import replace_render, reddit_link %>
|
||||
<% from r2.lib.template_helpers import replace_render, add_sr %>
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%
|
||||
_id = ("_%s" % thing.parent_name) if hasattr(thing, 'parent_name') else ''
|
||||
@@ -37,13 +38,13 @@
|
||||
%if thing.nextprev and (thing.prev or thing.next):
|
||||
<p class="nextprev"> ${_("view more:")} 
|
||||
%if thing.prev:
|
||||
<a rel="nofollow,prev" href="${reddit_link(thing.prev, url = True)}">${_("prev")}</a>
|
||||
${plain_link(_("prev"), thing.prev, rel="nofollow,prev")}
|
||||
%endif
|
||||
%if thing.prev and thing.next:
|
||||
 | 
|
||||
%endif
|
||||
%if thing.next:
|
||||
<a rel="nofollow,next" href="${reddit_link(thing.next, url = True)}">${_("next")}</a>
|
||||
${plain_link(_("next"), thing.next, rel="nofollow,prev")}
|
||||
%endif
|
||||
</p>
|
||||
%endif
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<% from r2.lib.template_helpers import replace_render, reddit_link %>
|
||||
<% from r2.lib.template_helpers import replace_render, add_sr %>
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<ul>
|
||||
%for a in thing.things:
|
||||
@@ -31,13 +32,13 @@
|
||||
%if thing.nextprev and (thing.prev or thing.next):
|
||||
<p class="nextprev"> ${_("view more:")} 
|
||||
%if thing.prev:
|
||||
<a href="${reddit_link(thing.prev, url = True)}">${_("prev")}</a>
|
||||
${plain_link(_("prev"), thing.prev)}
|
||||
%endif
|
||||
%if thing.prev and thing.next:
|
||||
 | 
|
||||
%endif
|
||||
%if thing.next:
|
||||
<a rel="nofollow" href="${reddit_link(thing.next, url = True)}">${_("next")}</a>
|
||||
${plain_link(_("next"), thing.next)}
|
||||
%endif
|
||||
</p>
|
||||
%endif
|
||||
|
||||
@@ -17,20 +17,21 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%def name="plain(selected = False)">
|
||||
${plain_link(thing.selected_title() if selected else thing.title,
|
||||
thing.path, _sr_path = thing.sr_path,
|
||||
thing.path, _sr_path = thing.sr_path, nocname = thing.nocname,
|
||||
target = thing.target,
|
||||
_class = thing.css_class, _id = thing._id)}
|
||||
</%def>
|
||||
|
||||
<%def name="js(selected = False)">
|
||||
${plain_link(thing.selected_title() if selected else thing.title,
|
||||
'/', _sr_path = False,
|
||||
'/', _sr_path = False, nocname = thing.nocname,
|
||||
_class = thing.css_class, _id = thing._id, onclick = thing.onclick)}
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link, text_with_links, img_link, separator"/>
|
||||
|
||||
@@ -17,18 +17,20 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<%def name="subreddit()">
|
||||
<span class="hover pagename redditname">
|
||||
<a href="${c.site.path}">${c.site.name}</a>
|
||||
${plain_link(c.site.name, c.site.path, _sr_path=False)}
|
||||
</span>
|
||||
</%def>
|
||||
|
||||
<%def name="reddits()">
|
||||
<span class="hover pagename redditname">
|
||||
<a href="/reddits/">${_("reddits")}</a>
|
||||
${plain_link(_("reddits"), "/reddits/", _sr_path=False)}
|
||||
</span>
|
||||
</%def>
|
||||
|
||||
@@ -37,5 +39,5 @@
|
||||
</%def>
|
||||
|
||||
<%def name="newpagelink()">
|
||||
<span class="newpagelink">reddit all? <a href="/new/">click here to find new links.</a></span>
|
||||
<span class="newpagelink">reddit all? ${plain_link("click here to find new links.", "/new/", _sr_path=False)}</span>
|
||||
</%def>
|
||||
|
||||
@@ -19,8 +19,7 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="language_tool, language_checkboxes"/>
|
||||
<%namespace file="utils.html" import="language_tool, language_checkboxes, plain_link"/>
|
||||
|
||||
<%def name="checkbox(text, name)">
|
||||
<input name="${name}" id="${name}" type="checkbox"\
|
||||
@@ -60,10 +59,15 @@
|
||||
%endif
|
||||
|
||||
<%
|
||||
if c.user_is_loggedin:
|
||||
action = "/post/options"
|
||||
if c.cname:
|
||||
param = "?cnameframe=1"
|
||||
else:
|
||||
action = "/post/unlogged_options"
|
||||
param = ""
|
||||
|
||||
if c.user_is_loggedin:
|
||||
action = "/post/options" + param
|
||||
else:
|
||||
action = "/post/unlogged_options" + param
|
||||
%>
|
||||
<form action="${action}" method="post" class="pretty-form short-text">
|
||||
<input type="hidden" name="uh" value="${c.modhash}" />
|
||||
@@ -75,7 +79,7 @@
|
||||
${language_tool(allow_blank = False, show_regions = True,
|
||||
default_lang = c.user.pref_lang)}
|
||||
 <span class="little gray hover">(*) ${_("incomplete")}
|
||||
 <a href="/feedback">${_("volunteer to translate")}</a></span>
|
||||
 ${plain_link(_("volunteer to translate"), "/feedback")}</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -156,7 +160,12 @@
|
||||
 <span class="little gray">(1 - ${g.max_comments})</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>${_("display options")}</th>
|
||||
<td class="prefright">
|
||||
${checkbox(_("allow reddits to show me custom styles"), "show_stylesheets")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("privacy options")}</th>
|
||||
<td class="prefright">
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import reddit_link
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.strings import strings
|
||||
%>
|
||||
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
${self.RenderPrintable()}
|
||||
|
||||
<%def name="admintagline()">
|
||||
@@ -148,7 +150,7 @@ ${self.RenderPrintable()}
|
||||
elif gray:
|
||||
author_cls += " gray"
|
||||
name = websafe(author.name)
|
||||
href = unsafe('href="/user/%s/"' % name)
|
||||
href = unsafe('href="%s"' % add_sr("/user/%s/" % name, sr_path = False))
|
||||
if c.user_is_admin: name += " (%d)" % (author.link_karma)
|
||||
%>
|
||||
<a id="author_${thing._fullname}" class="${author_cls}" ${href}>${name}</a>
|
||||
@@ -270,16 +272,15 @@ ${k.strip('_')}="${v}" \
|
||||
### originally in commentbutton
|
||||
<%def name="comment_button(name, fullname, link_text, num, link,\
|
||||
a_class='', title='', newwindow = False)">
|
||||
<% cls = "" if num == 0 else "comments" %>
|
||||
<a href="${reddit_link(link, url = True)}"
|
||||
id="${name}_${fullname}" class="${a_class} ${cls}"
|
||||
title="${title}"
|
||||
target="${'_blank' if newwindow else '_parent'}" >
|
||||
%if num > 0:
|
||||
${strings.number_label % (num,link_text)}\
|
||||
%else:
|
||||
${link_text}\
|
||||
%endif
|
||||
</a>
|
||||
<%
|
||||
cls = "" if num == 0 else "comments"
|
||||
|
||||
if num > 0:
|
||||
link_text = strings.number_label % (num, link_text)
|
||||
|
||||
target = '_blank' if newwindow else '_parent'
|
||||
%>
|
||||
${plain_link(link_text, link, id="%s_%s" % (name, fullname), _class="%s %s" % (a_class, cls), title=title, target=target)}
|
||||
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution
|
||||
## 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
|
||||
@@ -17,14 +17,14 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.filters import edit_comment_filter, unsafe, safemarkdown
|
||||
from r2.lib.utils import timesince
|
||||
%>
|
||||
<%namespace file="utils.html" import="submit_form"/>
|
||||
<%namespace file="utils.html" import="submit_form, plain_link"/>
|
||||
<% user = thing.user %>
|
||||
%if thing.user:
|
||||
<div class="raisedbox">
|
||||
@@ -65,9 +65,7 @@
|
||||
|
||||
%if c.user_is_loggedin and not thing.isMe:
|
||||
<li>
|
||||
<a href="/message/compose/?to=${thing.user.name}">
|
||||
${_("send message")}
|
||||
</a>
|
||||
${plain_link(_("send message"), "/message/compose/?to=%s" % thing.user.name)}
|
||||
</li>
|
||||
<li>
|
||||
<form action="/post/friend" method="post">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import reddit_link, static, join_urls, class_dict, path_info
|
||||
from r2.lib.template_helpers import add_sr, static, join_urls, class_dict, path_info
|
||||
from r2.lib.pages import SearchForm
|
||||
from pylons import request
|
||||
%>
|
||||
@@ -52,9 +52,16 @@
|
||||
%else:
|
||||
<link rel="stylesheet" href="${static(g.stylesheet)}"
|
||||
type="text/css" />
|
||||
%if c.site.stylesheet:
|
||||
<link rel="stylesheet" href="${static(c.site.stylesheet)}"
|
||||
type="text/css" />
|
||||
%if (not g.css_killswitch) and c.user.pref_show_stylesheets:
|
||||
%if c.site.stylesheet:
|
||||
<link rel="stylesheet" href="${static(c.site.stylesheet)}"
|
||||
type="text/css" />
|
||||
%endif
|
||||
%if c.site.stylesheet_contents:
|
||||
<link rel="stylesheet" href="${c.site.path}stylesheet?v=${c.site.stylesheet_hash}"
|
||||
title="applied_subreddit_stylesheet"
|
||||
type="text/css" />
|
||||
%endif
|
||||
%endif
|
||||
%endif
|
||||
|
||||
@@ -62,7 +69,7 @@
|
||||
type="image/x-icon" />
|
||||
%if thing.extension_handling:
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS"
|
||||
href="${reddit_link(join_urls(request.path,'.rss'), url=True)}" />
|
||||
href="${add_sr(join_urls(request.path,'.rss'))}" />
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
@@ -32,13 +32,13 @@
|
||||
${thing.footer_nav().render()}
|
||||
|
||||
<p class="wired">
|
||||
<a rel="nofollow" href="http://wired.com">
|
||||
<a rel="nofollow" target="_top" href="http://wired.com">
|
||||
<img src="${static('wired_w.png')}" alt="wired" /></a>
|
||||
<a rel="nofollow" href="http://wired.com">WIRED.com</a>-
|
||||
<a rel="nofollow" href="http://howto.wired.com">WIRED How-To</a>
|
||||
<a rel="nofollow" target="_top" href="http://wired.com">WIRED.com</a>-
|
||||
<a rel="nofollow" target="_top" href="http://howto.wired.com">WIRED How-To</a>
|
||||
</p>
|
||||
<p class="bottommenu">
|
||||
${text_with_links(_("Use of this site constitutes acceptance of our %(user_agreement)s and %(privacy_policy)s"), user_agreement= (_("User Agreement {Genitive}"), "http://reddit.com/help/useragreement"), privacy_policy = (_("Privacy Policy {Genitive}"), "http://reddit.com/help/privacypolicy"))}.
|
||||
${text_with_links(_("Use of this site constitutes acceptance of our %(user_agreement)s and %(privacy_policy)s"), nocname=True, user_agreement= (_("User Agreement {Genitive}"), "http://reddit.com/help/useragreement"), privacy_policy = (_("Privacy Policy {Genitive}"), "http://reddit.com/help/privacypolicy"))}.
|
||||
<% old = _("(c) 2008 CondeNet, Inc. All rights reserved.") %>
|
||||
${_("(c) %(year)d CondeNet, Inc. All rights reserved.") % \
|
||||
dict(year=2008)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution
|
||||
## 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
|
||||
@@ -23,6 +23,7 @@
|
||||
<%!
|
||||
from r2.lib.template_helpers import static
|
||||
from r2.models import Sub, FakeSubreddit
|
||||
from r2.models.subreddit import DefaultSR
|
||||
from r2.lib.pages import SearchForm
|
||||
%>
|
||||
<%namespace file="utils.html" import="plain_link, text_with_js, img_link, separator"/>
|
||||
@@ -60,9 +61,13 @@
|
||||
</div>
|
||||
|
||||
<div id="header-bottom-${'right' if c.lang_rtl else 'left'}">
|
||||
<a href="/">
|
||||
<img id="header-img" src="${c.site.header}" alt="${c.site.name}" />
|
||||
</a>
|
||||
<%
|
||||
if g.css_killswitch or not c.site.header:
|
||||
header_img = DefaultSR.header
|
||||
else:
|
||||
header_img = c.site.header
|
||||
%>
|
||||
${img_link(c.site.name, header_img, '/', _id = "header-img-a", img_id = 'header-img')}
|
||||
|
||||
##keeps the height of the header from varying when there isnt any content
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import reddit_link, static
|
||||
from r2.lib.template_helpers import add_sr, static
|
||||
%>
|
||||
|
||||
<form action="${reddit_link('/search', url=True)}" id="search">
|
||||
<form action="${add_sr('/search')}" id="search">
|
||||
<input type="text"
|
||||
%if thing.prev_search:
|
||||
value="${thing.prev_search}" style="color:black"
|
||||
|
||||
@@ -30,7 +30,7 @@ http://${g.domain}/goto?share=true&id=${thing.link._fullname}
|
||||
|
||||
<% from r2.lib.strings import strings, plurals %>${_("There are currently %(num_comments)s on this link. You can view them here:") % dict(num_comments = strings.number_label % (thing.link.num_comments, plurals.N_comments(thing.link.num_comments)))}
|
||||
|
||||
<% from r2.lib.template_helpers import reddit_link %>http://${g.domain}${reddit_link(thing.link.permalink, url=True)}
|
||||
<% from r2.lib.template_helpers import add_sr %>http://${g.domain}${add_sr(thing.link.permalink, url=True)}
|
||||
|
||||
___
|
||||
If you would not like to receive emails from reddit.com in the future, visit http://${g.domain}/mail/optout?x=${thing.msg_hash}
|
||||
@@ -17,7 +17,8 @@
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea id="share_to_${thing.link_name}" name="share_to" rows="4">
|
||||
<textarea id="share_to_${thing.link_name}"
|
||||
name="share_to" rows="4" cols="40">
|
||||
${unsafe('
'.join(websafe(e) for e in thing.emails))}
|
||||
</textarea>
|
||||
</td>
|
||||
@@ -55,7 +56,7 @@
|
||||
</span>
|
||||
</th>
|
||||
<td>
|
||||
<input name="replyto" type="text" size="30"
|
||||
<input name="replyto_${thing.link_name}" type="text" size="30"
|
||||
value="${c.user.email if hasattr(c.user, 'email') else ''}"/>
|
||||
</td>
|
||||
<td>
|
||||
@@ -74,7 +75,7 @@
|
||||
</span>
|
||||
</th>
|
||||
<td>
|
||||
<textarea id="message_${thing.link_name}" name="message" rows="4">
|
||||
<textarea id="message_${thing.link_name}" name="message" rows="4" cols="40">
|
||||
${c.user.name} from http://${g.domain}/ has shared a link with you.
|
||||
</textarea>
|
||||
</td>
|
||||
|
||||
@@ -17,15 +17,21 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<div class="sidebox ${thing.css_class}">
|
||||
<a class="morelink"
|
||||
%if not c.user_is_loggedin and thing.show_cover:
|
||||
onclick="return(showcover(true, 'redirect_${thing.link}'));"
|
||||
%endif
|
||||
href="${thing.link}">${thing.title} »</a>
|
||||
<%
|
||||
if not c.user_is_loggedin and thing.show_cover:
|
||||
onclick="return(showcover(true, 'redirect_%s'));" % thing.link
|
||||
else:
|
||||
onclick = None
|
||||
|
||||
nocname = thing.nocname
|
||||
%>
|
||||
${plain_link(thing.title,thing.link, _sr_path = False, _class="morelink", onclick=onclick, nocname=nocname)}
|
||||
%if thing.subtitles:
|
||||
<div class="spacer">
|
||||
%for subtitle in thing.subtitles:
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
@@ -25,6 +25,8 @@
|
||||
%>
|
||||
<%inherit file="printable.html"/>
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<%def name="numcol()">
|
||||
</%def>
|
||||
|
||||
@@ -32,9 +34,7 @@
|
||||
<%def name="entry()">
|
||||
<% fullname = thing._fullname %>
|
||||
<p class="titlerow">
|
||||
<a id="title_${fullname}" class="title" href="${thing.path}">
|
||||
${thing.title}
|
||||
</a>
|
||||
${plain_link(thing.title, thing.path, _class="title", _id="title_%s" % fullname)}
|
||||
<span class="domain">(${thing.path})</span>
|
||||
%if c.user_is_admin:
|
||||
${self.admintagline()}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
%>
|
||||
<%inherit file="printable.mobile"/>
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<%def name="numcol()">
|
||||
</%def>
|
||||
|
||||
@@ -32,9 +34,7 @@
|
||||
<%def name="entry()">
|
||||
<% fullname = thing._fullname %>
|
||||
<p>
|
||||
<a class="title" href="${thing.path}">
|
||||
${thing.title}
|
||||
</a>
|
||||
${plain_link(thing.title, thing.path, _class="title")}
|
||||
<span class="domain">(${thing.path})</span>
|
||||
</p>
|
||||
%if thing.description:
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<div class="subredditbox">
|
||||
<form method="post" action="/post/subscriptions" id="sr_subscriptions"
|
||||
@@ -50,7 +51,13 @@
|
||||
onchange="return change_sr('${sr._fullname}');"
|
||||
id="sr_sel_chx_${sr._fullname}"/>
|
||||
%endif
|
||||
<a href="${sr.path or '/'}">${sr.name}</a>
|
||||
<%
|
||||
if sr.path:
|
||||
path = sr.path
|
||||
else:
|
||||
path = '/'
|
||||
%>
|
||||
${plain_link(sr.name, path, _sr_path=False, nocname=True)}
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
@@ -58,7 +65,6 @@
|
||||
%endfor
|
||||
<div class="clear"></div>
|
||||
</form>
|
||||
<a class="morelink" href="/reddits/">
|
||||
${_("more")} »
|
||||
</a>
|
||||
${plain_link(unsafe('%s »' % _("more")), "/reddits/", _class="morelink",
|
||||
fmt='%s', _sr_path=False, nocname=True)}
|
||||
</div>
|
||||
|
||||
119
r2/r2/templates/subredditstylesheet.html
Normal file
119
r2/r2/templates/subredditstylesheet.html
Normal file
@@ -0,0 +1,119 @@
|
||||
## 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-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%!
|
||||
from r2.lib.filters import keep_space
|
||||
from r2.lib.template_helpers import add_sr
|
||||
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field, language_tool"/>
|
||||
|
||||
<div class="stylesheet-customize-container">
|
||||
|
||||
<form
|
||||
onsubmit="return stylesheetSave(this, 'subreddit_stylesheet')"
|
||||
name="subreddit_stylesheet" id="subreddit_stylesheet"
|
||||
class="pretty-form sr-form"
|
||||
action="/post/subreddit_stylesheet" method="post" >
|
||||
|
||||
<input type="hidden" name="r" value="${thing.site.name}" />
|
||||
<input type="hidden" name="op" value="save" />
|
||||
|
||||
<h2>${_("stylesheet")}</h2>
|
||||
<div class="sheets">
|
||||
<div style="width: 100%" class="col">
|
||||
<div>
|
||||
<textarea
|
||||
rows="20"
|
||||
cols="20"
|
||||
id="stylesheet_contents"
|
||||
name="stylesheet_contents"
|
||||
>
|
||||
${keep_space(thing.stylesheet_contents) or ''}
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:none" class="col">
|
||||
<div>
|
||||
<textarea
|
||||
rows="20"
|
||||
cols="20"
|
||||
id="default_stylesheet"
|
||||
name="default_stylesheet"
|
||||
readonly="readonly"
|
||||
>
|
||||
${keep_space(g.default_stylesheet)}
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearleft"></div>
|
||||
<div class="buttons">
|
||||
<button class="btn right " id="show_default_stylesheet"
|
||||
value="some_button"
|
||||
onclick="return showDefaultStylesheet();">
|
||||
${_('show the default stylesheet')}
|
||||
</button>
|
||||
<button class="btn right" id="hide_default_stylesheet"
|
||||
value="some_button"
|
||||
style="display: none;"
|
||||
onclick="return hideDefaultStylesheet();">
|
||||
${_('hide the default stylesheet')}
|
||||
</button>
|
||||
<button class="btn" name="save" type="submit">${_("save")}</button>
|
||||
<button class="btn" name="preview" type="button"
|
||||
onclick="stylesheetPreview('subreddit_stylesheet','stylesheet_contents');">${_("preview")}</button>
|
||||
|
||||
<span id="status" class="error"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="errors">
|
||||
<h2 id="error-header" style="display:none">${_("errors")}</h2>
|
||||
<ul id="validation-errors"><li style="display:none"></li>
|
||||
<!-- populated from AJAX requests to /api/subreddit_stylesheet -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
</form>
|
||||
|
||||
<div id="preview-table" style="display:none">
|
||||
<h2><a name="preview">${_("preview")}</a></h2>
|
||||
<table>
|
||||
<tr>
|
||||
<th>${_("normal link")}</th>
|
||||
<td id="preview_link_normal"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("compressed link")}</th>
|
||||
<td id="preview_link_compressed"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("link with thumbnail")}</th>
|
||||
<td id="preview_link_media"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>${_("comment")}</th>
|
||||
<td id="preview_comment"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%
|
||||
|
||||
31
r2/r2/templates/uploadedimage.html
Normal file
31
r2/r2/templates/uploadedimage.html
Normal file
@@ -0,0 +1,31 @@
|
||||
## 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-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
parent.completedUploadHeaderImage('${thing.status}','${thing.img_src}', '${thing.op}');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
you shouldn't be here
|
||||
</body>
|
||||
</html>
|
||||
@@ -17,15 +17,17 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
##<h1>awards</h1>
|
||||
##<table class='stats'>
|
||||
##<tr><td></td><th>today</th></tr>
|
||||
###for a in $awards
|
||||
## #set u = $user_objs[a["uid"]]
|
||||
## <tr><td class='ri' style="padding-right: 10px">$a['award']</td><td class='k'><a href="/user/$u.name/">$u.name</a> ($sanekarma($u.pop))</td></tr>
|
||||
## <tr><td class='ri' style="padding-right: 10px">$a['award']</td><td class='k'>${plain_link(u.name, "/user/%s" % u.name)} ($sanekarma($u.pop))</td></tr>
|
||||
###end for
|
||||
##</table>
|
||||
###end if
|
||||
@@ -36,7 +38,7 @@
|
||||
<tr><th colspan="2">${_("today")}</th></tr>
|
||||
%for user, change in thing.top_day:
|
||||
<tr>
|
||||
<td class="k"><a href="/user/${user.name}/">${user.name}</a> (${user.link_karma})</td>
|
||||
<td class="k">${plain_link(user.name, "/user/%s" % user.name)} (${user.link_karma})</td>
|
||||
<td class="ri">+${change}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
@@ -46,7 +48,7 @@
|
||||
<tr><th colspan="2">${_("this week")}</th></tr>
|
||||
%for user, change in thing.top_week:
|
||||
<tr>
|
||||
<td class="k"><a href="/user/${user.name}/">${user.name}</a> (${user.link_karma})</td>
|
||||
<td class="k">${plain_link(user.name, "/user/%s" % user.name)} (${user.link_karma})</td>
|
||||
<td class="ri">+${change}</td>
|
||||
</tr>
|
||||
%endfor
|
||||
@@ -56,7 +58,7 @@
|
||||
<tr><th colspan="2">${_("all-time")}</th></tr>
|
||||
%for user in thing.top_users:
|
||||
<tr>
|
||||
<td class="k"><a href="/user/${user.name}/">${user.name}</a> (${user.link_karma})</td>
|
||||
<td class="k">${plain_link(user.name, "/user/%s" % user.name)} (${user.link_karma})</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
################################################################################
|
||||
|
||||
<%namespace file="printable.html" import="yes_no_button" />
|
||||
@@ -39,7 +39,7 @@
|
||||
(<b>${thing.user.safe_karma}</b>)
|
||||
</span>
|
||||
%elif c.user_is_loggedin and name == "sendmessage" and c.user != thing.user:
|
||||
<a href="/message/compose?to=${thing.user.name}">${_("send message")}</a>
|
||||
${plain_link(_("send message"), "/message/compose?to=%s" % (thing.user.name))}
|
||||
%elif name == "remove" and thing.editable:
|
||||
${yes_no_button("remove", thing.name, _("remove"),
|
||||
"return deletetoggle(this, UserTable.del('%s', '%s'));"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution.
|
||||
## 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
|
||||
@@ -17,13 +17,13 @@
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved."
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.controllers.errors import errors
|
||||
from r2.lib.filters import spaceCompress
|
||||
from r2.lib.template_helpers import reddit_link
|
||||
from r2.lib.filters import spaceCompress, unsafe
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.utils import cols
|
||||
%>
|
||||
<%def name="tags(**kw)">
|
||||
@@ -94,23 +94,42 @@ ${errors.get(name).message}
|
||||
</script>
|
||||
</%def>
|
||||
|
||||
<%def name="img_link(title, img, path, _id='', **kw)">
|
||||
<%def name="img_link(link_text, img, path, _id='', target='', img_id = None, **kw)">
|
||||
<%
|
||||
if (not target or target == '_parent') and c.cname:
|
||||
target = '_top'
|
||||
if target:
|
||||
kw['target'] = target
|
||||
|
||||
path = add_sr(path, sr_path = False)
|
||||
kw['target'] = target
|
||||
%>
|
||||
<%call expr="_a(href=path, _id=_id, **kw)">
|
||||
<img src="${img}" alt="${title}"/>
|
||||
<img ${("id='%s'" % img_id) if img_id else ''} src="${img}" alt="${link_text}"/>
|
||||
</%call>
|
||||
</%def>
|
||||
|
||||
<%def name="plain_link(title, path, _sr_path = True, fmt='', **kw)">
|
||||
<% link = _a_buffered(title, href=reddit_link(path, url=_sr_path), **kw) %>
|
||||
<%def name="plain_link(link_text, path, _sr_path = True, nocname=False, fmt='', target='', **kw)">
|
||||
<%
|
||||
if (not target or target == '_parent') and c.cname:
|
||||
target = '_top'
|
||||
if c.cname and path.startswith('http://'):
|
||||
target = '_top'
|
||||
if target:
|
||||
kw['target'] = target
|
||||
|
||||
link = _a_buffered(link_text, href=add_sr(path, sr_path=_sr_path, nocname=nocname), **kw)
|
||||
%>
|
||||
|
||||
${unsafe((fmt % link) if fmt else link)}
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="text_with_links(txt, _sr_path = False, **kw)">
|
||||
<%def name="text_with_links(txt, _sr_path = False, nocname=False, **kw)">
|
||||
<%
|
||||
from r2.lib.filters import _force_unicode
|
||||
for key, (text, href) in kw.iteritems():
|
||||
kw[key]=spaceCompress(capture(plain_link, text, href, _sr_path=_sr_path))
|
||||
kw[key]=spaceCompress(capture(plain_link, text, href, _sr_path=_sr_path, nocname=nocname))
|
||||
txt = _force_unicode(txt) % kw
|
||||
txt = txt.replace(" <", " <").replace("> ", "> ")
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
## "The contents of this file are subject to the Common Public Attribution
|
||||
## 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
|
||||
@@ -21,7 +21,9 @@
|
||||
################################################################################
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
<%
|
||||
domain = c.site.domain if c.cname else c.domain
|
||||
%>
|
||||
function escapeHTML(text) {
|
||||
var div = document.createElement('div');
|
||||
var text = document.createTextNode(text);
|
||||
@@ -39,11 +41,11 @@ function update() {
|
||||
f = document.forms.widget;
|
||||
which = getrval(f.which);
|
||||
if (which == "all") {
|
||||
url = "http://${c.domain}/" + f.what.value + "/.embed?limit=" +
|
||||
url = "http://${domain}/" + f.what.value + "/.embed?limit=" +
|
||||
f.num.value + "&t=" + f.when.value;
|
||||
} else if (which == "one") {
|
||||
if (!f.who2.value) return;
|
||||
url = "http://${c.domain}/user/"+f.who2.value+"/"+
|
||||
url = "http://${domain}/user/"+f.who2.value+"/"+
|
||||
f.where2.value+".embed?limit=" + f.num.value +
|
||||
"&sort="+f.what2.value;
|
||||
} else {
|
||||
@@ -67,7 +69,7 @@ function update() {
|
||||
<div id="preview">
|
||||
<span>preview</span>
|
||||
<div id="previewbox">
|
||||
<script src="http://${c.domain}/.embed?limit=5" type="text/javascript"></script>
|
||||
<script src="http://${domain}/.embed?limit=5" type="text/javascript"></script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -139,7 +141,7 @@ function update() {
|
||||
|
||||
<p>
|
||||
<textarea rows="5" cols="50" id="codebox">
|
||||
<script src="http://${c.domain}/.embed?limit=5" type="text/javascript"></script>
|
||||
<script src="http://${domain}/.embed?limit=5" type="text/javascript"></script>
|
||||
</textarea>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -83,6 +83,7 @@ setup(
|
||||
"simplejson",
|
||||
"SQLAlchemy==0.3.10",
|
||||
"BeautifulSoup >= 3",
|
||||
"cssutils",
|
||||
"chardet",
|
||||
"psycopg2",
|
||||
"py_interface"],
|
||||
|
||||
Reference in New Issue
Block a user