mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-28 08:17:58 -05:00
Epic Caching Update. Major features (mostly in r2.lib.wrapped)
* Wrapped -> Templated in cases when there is no thing to be wrapped * cachable templates out of cachable pieces which can be reused * auto-generation of cache keys for cachable content * c.render_style used to propagate current style to children * cut down on the number of c and g variables in thing templates. * buttons in printable.html now in printablebuttons.html with handler classes in r2.lib.pages.things (planned destination of add_props).
This commit is contained in:
@@ -23,7 +23,7 @@
|
||||
# Jacascript files to be compressified
|
||||
js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js
|
||||
# CSS targets
|
||||
css_targets = reddit.css reddit-ie6-hax.css reddit-ie7-hax.css
|
||||
css_targets = reddit.css reddit-ie6-hax.css reddit-ie7-hax.css mobile.css
|
||||
|
||||
SED=sed
|
||||
|
||||
|
||||
@@ -24,6 +24,9 @@ import os
|
||||
#import pylons.config
|
||||
from pylons import config
|
||||
|
||||
import mimetypes
|
||||
mimetypes.init()
|
||||
|
||||
import webhelpers
|
||||
|
||||
from r2.config.routing import make_map
|
||||
|
||||
@@ -39,8 +39,7 @@ from r2.lib.jsontemplates import api_type
|
||||
#middleware stuff
|
||||
from r2.lib.html_source import HTMLValidationParser
|
||||
from cStringIO import StringIO
|
||||
import sys, tempfile, urllib, re, os, sha
|
||||
|
||||
import sys, tempfile, urllib, re, os, sha, subprocess
|
||||
|
||||
#from pylons.middleware import error_mapper
|
||||
def error_mapper(code, message, environ, global_conf=None, **kw):
|
||||
@@ -94,17 +93,70 @@ class DebugMiddleware(object):
|
||||
if debug and self.keyword in args.keys():
|
||||
prof_arg = args.get(self.keyword)
|
||||
prof_arg = urllib.unquote(prof_arg) if prof_arg else None
|
||||
return self.filter(foo, prof_arg = prof_arg)
|
||||
r = self.filter(foo, prof_arg = prof_arg)
|
||||
if isinstance(r, Response):
|
||||
return r(environ, start_response)
|
||||
return r
|
||||
return self.app(environ, start_response)
|
||||
|
||||
def filter(self, execution_func, prof_arg = None):
|
||||
pass
|
||||
|
||||
|
||||
class ProfileGraphMiddleware(DebugMiddleware):
|
||||
def __init__(self, app):
|
||||
DebugMiddleware.__init__(self, app, 'profile-graph')
|
||||
|
||||
def filter(self, execution_func, prof_arg = None):
|
||||
# put thie imports here so the app doesn't choke if profiling
|
||||
# is not present (this is a debug-only feature anyway)
|
||||
import cProfile as profile
|
||||
from pstats import Stats
|
||||
from r2.lib.contrib import gprof2dot
|
||||
# profiling needs an actual file to dump to. Everything else
|
||||
# can be mitigated with streams
|
||||
tmpfile = tempfile.NamedTemporaryFile()
|
||||
dotfile = StringIO()
|
||||
# simple cutoff validation
|
||||
try:
|
||||
cutoff = .01 if prof_arg is None else float(prof_arg)/100
|
||||
except ValueError:
|
||||
cutoff = .01
|
||||
try:
|
||||
# profile the code in the current context
|
||||
profile.runctx('execution_func()',
|
||||
globals(), locals(), tmpfile.name)
|
||||
# parse the data
|
||||
parser = gprof2dot.PstatsParser(tmpfile.name)
|
||||
prof = parser.parse()
|
||||
# remove nodes and edges with less than cutoff work
|
||||
prof.prune(cutoff, cutoff)
|
||||
# make the dotfile
|
||||
dot = gprof2dot.DotWriter(dotfile)
|
||||
dot.graph(prof, gprof2dot.TEMPERATURE_COLORMAP)
|
||||
# convert the dotfile to PNG in local stdout
|
||||
proc = subprocess.Popen("dot -Tpng",
|
||||
shell = True,
|
||||
stdin =subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
out, error = proc.communicate(input = dotfile.getvalue())
|
||||
# generate the response
|
||||
r = Response()
|
||||
r.status_code = 200
|
||||
r.headers['content-type'] = "image/png"
|
||||
r.content = out
|
||||
return r
|
||||
finally:
|
||||
tmpfile.close()
|
||||
|
||||
class ProfilingMiddleware(DebugMiddleware):
|
||||
def __init__(self, app):
|
||||
DebugMiddleware.__init__(self, app, 'profile')
|
||||
|
||||
def filter(self, execution_func, prof_arg = None):
|
||||
# put thie imports here so the app doesn't choke if profiling
|
||||
# is not present (this is a debug-only feature anyway)
|
||||
import cProfile as profile
|
||||
from pstats import Stats
|
||||
|
||||
@@ -383,6 +435,10 @@ class RequestLogMiddleware(object):
|
||||
return r
|
||||
|
||||
class LimitUploadSize(object):
|
||||
"""
|
||||
Middleware for restricting the size of uploaded files (such as
|
||||
image files for the CSS editing capability).
|
||||
"""
|
||||
def __init__(self, app, max_size=1024*500):
|
||||
self.app = app
|
||||
self.max_size = max_size
|
||||
@@ -399,6 +455,29 @@ class LimitUploadSize(object):
|
||||
|
||||
return self.app(environ, start_response)
|
||||
|
||||
|
||||
class CleanupMiddleware(object):
|
||||
"""
|
||||
Put anything here that should be called after every other bit of
|
||||
middleware. This currently includes the code for removing
|
||||
duplicate headers (such as multiple cookie setting). The behavior
|
||||
here is to disregard all but the last record.
|
||||
"""
|
||||
def __init__(self, app):
|
||||
self.app = app
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
def custom_start_response(status, headers, exc_info = None):
|
||||
fixed = []
|
||||
seen = set()
|
||||
for head, val in reversed(headers):
|
||||
head = head.lower()
|
||||
if head not in seen:
|
||||
fixed.insert(0, (head, val))
|
||||
seen.add(head)
|
||||
return start_response(status, fixed, exc_info)
|
||||
return self.app(environ, custom_start_response)
|
||||
|
||||
#god this shit is disorganized and confusing
|
||||
class RedditApp(PylonsBaseWSGIApp):
|
||||
def find_controller(self, controller):
|
||||
@@ -439,7 +518,11 @@ def make_app(global_conf, full_stack=True, **app_conf):
|
||||
|
||||
# CUSTOM MIDDLEWARE HERE (filtered by the error handling middlewares)
|
||||
|
||||
# last thing first from here down
|
||||
app = CleanupMiddleware(app)
|
||||
|
||||
app = LimitUploadSize(app)
|
||||
app = ProfileGraphMiddleware(app)
|
||||
app = ProfilingMiddleware(app)
|
||||
app = SourceViewMiddleware(app)
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ def api(type, cls):
|
||||
tpm.add_handler(type, 'api-html', cls())
|
||||
|
||||
# blanket fallback rule
|
||||
api('wrapped', NullJsonTemplate)
|
||||
api('templated', NullJsonTemplate)
|
||||
|
||||
# class specific overrides
|
||||
api('link', LinkJsonTemplate)
|
||||
|
||||
@@ -30,14 +30,13 @@ from r2.models import *
|
||||
from r2.models.subreddit import Default as DefaultSR
|
||||
import r2.models.thing_changes as tc
|
||||
|
||||
from r2.controllers import ListingController
|
||||
|
||||
from r2.lib.utils import get_title, sanitize_url, timeuntil, set_last_modified
|
||||
from r2.lib.utils import query_string, to36, timefromnow, link_from_url
|
||||
from r2.lib.wrapped import Wrapped
|
||||
from r2.lib.pages import FriendList, ContributorList, ModList, \
|
||||
BannedList, BoringPage, FormPage, NewLink, CssError, UploadedImage, \
|
||||
ClickGadget
|
||||
from r2.lib.pages.things import wrap_links, default_thing_wrapper
|
||||
|
||||
from r2.lib.menus import CommentSortMenu
|
||||
from r2.lib.normalized_hot import expire_hot
|
||||
@@ -84,8 +83,7 @@ class ApiController(RedditController):
|
||||
return abort(404, 'not found')
|
||||
|
||||
links = link_from_url(request.params.get('url'), filter_spam = False)
|
||||
builder = IDBuilder([link._fullname for link in links], num = count)
|
||||
listing = LinkListing(builder, nextprev = False).listing()
|
||||
listing = wrap_links(links, num = count)
|
||||
return BoringPage(_("API"), content = listing).render()
|
||||
|
||||
@validatedForm(VCaptcha(),
|
||||
@@ -574,8 +572,7 @@ class ApiController(RedditController):
|
||||
if kind == 'link':
|
||||
set_last_modified(item, 'comments')
|
||||
|
||||
wrapper = make_wrapper(ListingController.builder_wrapper,
|
||||
expand_children = True)
|
||||
wrapper = default_thing_wrapper(expand_children = True)
|
||||
jquery(".content").replace_things(item, True, True, wrap = wrapper)
|
||||
|
||||
@validatedForm(VUser(),
|
||||
@@ -1116,7 +1113,8 @@ class ApiController(RedditController):
|
||||
if children:
|
||||
builder = CommentBuilder(link, CommentSortMenu.operator(sort),
|
||||
children)
|
||||
items = builder.get_items(starting_depth = depth, num = 20)
|
||||
listing = Listing(builder, nextprev = False)
|
||||
items = listing.get_items(starting_depth = depth, num = 20)
|
||||
def _children(cur_items):
|
||||
items = []
|
||||
for cm in cur_items:
|
||||
@@ -1286,12 +1284,8 @@ class ApiController(RedditController):
|
||||
@validatedForm(links = VByName('links', thing_cls = Link, multiple = True),
|
||||
show = VByName('show', thing_cls = Link, multiple = False))
|
||||
def POST_fetch_links(self, form, jquery, links, show):
|
||||
b = IDBuilder([l._fullname for l in links],
|
||||
wrap = ListingController.builder_wrapper)
|
||||
l = OrganicListing(b)
|
||||
l.num_margin = 0
|
||||
l.mid_margin = 0
|
||||
|
||||
l = wrap_links(links, listing_cls = OrganicListing,
|
||||
num_margin = 0, mid_margin = 0)
|
||||
jquery(".content").replace_things(l, stubs = True)
|
||||
|
||||
if show:
|
||||
@@ -1473,5 +1467,6 @@ class ApiController(RedditController):
|
||||
if not link:
|
||||
abort(404, 'not found')
|
||||
|
||||
wrapped = IDBuilder([link._fullname]).get_items()[0][0]
|
||||
wrapped = wrap_links(link)
|
||||
wrapped = list(wrapped)[0]
|
||||
return spaceCompress(websafe(wrapped.link_child.content()))
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import Button, ButtonNoBody, ButtonEmbed, ButtonLite, \
|
||||
ButtonDemoPanel, WidgetDemoPanel, Bookmarklets, BoringPage, Socialite
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.models import *
|
||||
from r2.lib.strings import Score
|
||||
from r2.lib.utils import tup, query_string
|
||||
from pylons import c, request
|
||||
from validator import *
|
||||
@@ -39,7 +39,7 @@ class ButtonsController(RedditController):
|
||||
except ValueError:
|
||||
return 1
|
||||
|
||||
def get_wrapped_link(self, url, link = None):
|
||||
def get_wrapped_link(self, url, link = None, wrapper = None):
|
||||
try:
|
||||
if link:
|
||||
links = [link]
|
||||
@@ -51,24 +51,20 @@ class ButtonsController(RedditController):
|
||||
links = []
|
||||
|
||||
if links:
|
||||
# cache the render style and reset it to html
|
||||
rs = c.render_style
|
||||
c.render_style = 'html'
|
||||
link_builder = IDBuilder([l._fullname for l in links],
|
||||
wrap=ListingController.builder_wrapper)
|
||||
|
||||
# link_listing will be the one-element listing at the top
|
||||
link_listing = LinkListing(link_builder,
|
||||
nextprev=False).listing()
|
||||
|
||||
# reset the render style
|
||||
c.render_style = rs
|
||||
links = link_listing.things
|
||||
kw = {}
|
||||
if wrapper:
|
||||
links = wrap_links(links, wrapper = wrapper)
|
||||
else:
|
||||
links = wrap_links(links)
|
||||
links = list(links)
|
||||
links = max(links, key = lambda x: x._score) if links else None
|
||||
if not links and wrapper:
|
||||
return wrapper(None)
|
||||
return links
|
||||
# note: even if _by_url successed or a link was passed in,
|
||||
# it is possible link_listing.things is empty if the
|
||||
# link(s) is/are members of a private reddit
|
||||
# return the link with the highest score (if more than 1)
|
||||
return max(links, key = lambda x: x._score) if links else None
|
||||
except:
|
||||
#we don't want to return 500s in other people's pages.
|
||||
import traceback
|
||||
@@ -84,43 +80,31 @@ class ButtonsController(RedditController):
|
||||
width = VInt('width', 0, 800),
|
||||
link = VByName('id'))
|
||||
def GET_button_content(self, url, title, css, vote, newwindow, width, link):
|
||||
|
||||
|
||||
|
||||
# no buttons on domain listings
|
||||
if isinstance(c.site, DomainSR):
|
||||
c.site = Default
|
||||
return self.redirect(request.path + query_string(request.GET))
|
||||
|
||||
l = self.get_wrapped_link(url, link)
|
||||
if l: url = l.url
|
||||
|
||||
#disable css hack
|
||||
if (css != 'http://blog.wired.com/css/redditsocial.css' and
|
||||
css != 'http://www.wired.com/css/redditsocial.css'):
|
||||
css = None
|
||||
|
||||
bt = self.buttontype()
|
||||
if bt == 1:
|
||||
score_fmt = Score.safepoints
|
||||
else:
|
||||
score_fmt = Score.number_only
|
||||
|
||||
page_handler = Button
|
||||
if not vote:
|
||||
page_handler = ButtonNoBody
|
||||
wrapper = make_wrapper(Button if vote else ButtonNoBody,
|
||||
url = url,
|
||||
target = "_new" if newwindow else "_parent",
|
||||
title = title, vote = vote, bgcolor = c.bgcolor,
|
||||
width = width, css = css,
|
||||
button = self.buttontype())
|
||||
|
||||
if newwindow:
|
||||
target = "_new"
|
||||
else:
|
||||
target = "_parent"
|
||||
|
||||
res = page_handler(button=bt, css=css,
|
||||
score_fmt = score_fmt, link = l,
|
||||
url=url, title=title,
|
||||
vote = vote, target = target,
|
||||
bgcolor=c.bgcolor, width=width).render()
|
||||
l = self.get_wrapped_link(url, link, wrapper)
|
||||
res = l.render()
|
||||
c.response.content = spaceCompress(res)
|
||||
return c.response
|
||||
|
||||
|
||||
|
||||
@validate(buttontype = VInt('t', 1, 5),
|
||||
url = VSanitizedUrl("url"),
|
||||
@@ -138,7 +122,8 @@ class ButtonsController(RedditController):
|
||||
return c.response
|
||||
|
||||
buttontype = buttontype or 1
|
||||
width, height = ((120, 22), (51, 69), (69, 52), (51, 52), (600, 52))[min(buttontype - 1, 4)]
|
||||
width, height = ((120, 22), (51, 69), (69, 52),
|
||||
(51, 52), (600, 52))[min(buttontype - 1, 4)]
|
||||
if _width: width = _width
|
||||
if _height: height = _height
|
||||
|
||||
@@ -147,32 +132,36 @@ class ButtonsController(RedditController):
|
||||
height=height,
|
||||
url = url,
|
||||
referer = request.referer).render()
|
||||
# we doing want the JS to be cached!
|
||||
# we doing want the JS to be cached (it is referer dependent)
|
||||
c.used_cache = True
|
||||
return self.sendjs(bjs, callback='', escape=False)
|
||||
|
||||
@validate(buttonimage = VInt('i', 0, 14),
|
||||
title = nop('title'),
|
||||
url = VSanitizedUrl('url'),
|
||||
newwindow = VBoolean('newwindow', default = False),
|
||||
styled = VBoolean('styled', default=True))
|
||||
def GET_button_lite(self, buttonimage, url, styled, newwindow):
|
||||
def GET_button_lite(self, buttonimage, title, url, styled, newwindow):
|
||||
c.render_style = 'js'
|
||||
c.response_content_type = 'text/javascript; charset=UTF-8'
|
||||
|
||||
if not url:
|
||||
url = request.referer
|
||||
if newwindow:
|
||||
target = "_new"
|
||||
else:
|
||||
target = "_parent"
|
||||
# we don't want the JS to be cached if the referer was involved.
|
||||
c.used_cache = True
|
||||
|
||||
l = self.get_wrapped_link(url)
|
||||
image = 1 if buttonimage is None else buttonimage
|
||||
def builder_wrapper(thing = None):
|
||||
kw = {}
|
||||
if not thing:
|
||||
kw['url'] = url
|
||||
kw['title'] = title
|
||||
return ButtonLite(thing,
|
||||
image = 1 if buttonimage is None else buttonimage,
|
||||
target = "_new" if newwindow else "_parent",
|
||||
styled = styled, **kw)
|
||||
|
||||
bjs = ButtonLite(image = image, link = l, url = l.url if l else url,
|
||||
target = target, styled = styled).render()
|
||||
# we don't want the JS to be cached!
|
||||
c.used_cache = True
|
||||
return self.sendjs(bjs, callback='', escape=False)
|
||||
bjs = self.get_wrapped_link(url, wrapper = builder_wrapper)
|
||||
return self.sendjs(bjs.render(), callback='', escape=False)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from reddit_base import RedditController, base_listing
|
||||
from r2 import config
|
||||
from r2.models import *
|
||||
from r2.lib.pages import *
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.lib.menus import *
|
||||
from r2.lib.utils import to36, sanitize_url, check_cheating, title_to_url
|
||||
from r2.lib.utils import query_string, UrlParser, link_from_url, link_duplicates
|
||||
@@ -513,12 +514,10 @@ class FrontController(RedditController):
|
||||
if links and len(links) == 1:
|
||||
return self.redirect(links[0].already_submitted_link)
|
||||
elif links:
|
||||
builder = IDBuilder([link._fullname for link in links])
|
||||
listing = LinkListing(builder, nextprev=False).listing()
|
||||
infotext = (strings.multiple_submitted
|
||||
% links[0].resubmit_link())
|
||||
res = BoringPage(_("seen it"),
|
||||
content = listing,
|
||||
content = wrap_links(links),
|
||||
infotext = infotext).render()
|
||||
return res
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from validator import *
|
||||
|
||||
from r2.models import *
|
||||
from r2.lib.pages import *
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.lib.menus import NewMenu, TimeMenu, SortMenu, RecSortMenu, ControversyTimeMenu
|
||||
from r2.lib.rising import get_rising
|
||||
from r2.lib.wrapped import Wrapped
|
||||
@@ -148,17 +149,7 @@ class ListingController(RedditController):
|
||||
"""Contents of the right box when rendering"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def builder_wrapper(thing):
|
||||
w = Wrapped(thing)
|
||||
|
||||
if isinstance(thing, Link):
|
||||
if thing.promoted:
|
||||
w = Wrapped(thing)
|
||||
w.render_class = PromotedLink
|
||||
w.rowstyle = 'promoted link'
|
||||
|
||||
return w
|
||||
builder_wrapper = staticmethod(default_thing_wrapper())
|
||||
|
||||
def GET_listing(self, **env):
|
||||
return self.build_listing(**env)
|
||||
|
||||
@@ -23,6 +23,7 @@ from validator import *
|
||||
from pylons.i18n import _
|
||||
from r2.models import *
|
||||
from r2.lib.pages import *
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.lib.menus import *
|
||||
from r2.controllers import ListingController
|
||||
|
||||
@@ -40,16 +41,10 @@ class PromoteController(RedditController):
|
||||
|
||||
@validate(VSponsor())
|
||||
def GET_current_promos(self):
|
||||
current_list = get_promoted()
|
||||
|
||||
b = IDBuilder(current_list)
|
||||
|
||||
render_list = b.get_items()[0]
|
||||
|
||||
render_list = list(wrap_links(get_promoted()))
|
||||
for x in render_list:
|
||||
if x.promote_until:
|
||||
x.promote_expires = timetext(datetime.now(g.tz) - x.promote_until)
|
||||
|
||||
page = PromotePage('current_promos',
|
||||
content = PromotedLinks(render_list))
|
||||
|
||||
@@ -65,12 +60,8 @@ class PromoteController(RedditController):
|
||||
link = VLink('link'))
|
||||
def GET_edit_promo(self, link):
|
||||
sr = Subreddit._byID(link.sr_id)
|
||||
|
||||
names = [link._fullname]
|
||||
builder = IDBuilder(names, wrap = ListingController.builder_wrapper)
|
||||
listing = LinkListing(builder,
|
||||
show_nums = False, nextprev = False)
|
||||
rendered = listing.listing().render()
|
||||
listing = wrap_links(link)
|
||||
rendered = listing.render()
|
||||
|
||||
timedeltatext = ''
|
||||
if link.promote_until:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
from reddit_base import RedditController
|
||||
from r2.lib.pages import *
|
||||
from r2.models import *
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.lib.menus import CommentSortMenu
|
||||
from r2.lib.filters import spaceCompress, safemarkdown
|
||||
from r2.lib.memoize import memoize
|
||||
@@ -151,26 +152,16 @@ class ToolbarController(RedditController):
|
||||
if not link.subreddit_slow.can_view(c.user):
|
||||
abort(403, 'forbidden')
|
||||
|
||||
def builder_wrapper(cm):
|
||||
w = Wrapped(cm)
|
||||
w.render_class = StarkComment
|
||||
w.target = "_top"
|
||||
return w
|
||||
|
||||
link_builder = IDBuilder((link._fullname,))
|
||||
link_listing = LinkListing(link_builder, nextprev=False).listing()
|
||||
links = link_listing.things[0],
|
||||
links = list(wrap_links(link))
|
||||
if not links:
|
||||
# they aren't allowed to see this link
|
||||
return self.abort(403, 'forbidden')
|
||||
link = links[0]
|
||||
|
||||
res = FrameToolbar(link = link,
|
||||
title = link.title,
|
||||
url = link.url)
|
||||
|
||||
wrapper = make_wrapper(render_class = StarkComment,
|
||||
target = "_top")
|
||||
b = TopCommentBuilder(link, CommentSortMenu.operator('top'),
|
||||
wrap = builder_wrapper)
|
||||
wrap = wrapper)
|
||||
|
||||
listing = NestedListing(b, num = 10, # TODO: add config var
|
||||
parent_name = link._fullname)
|
||||
@@ -180,7 +171,8 @@ class ToolbarController(RedditController):
|
||||
|
||||
md_bar = safemarkdown(raw_bar, target="_top")
|
||||
|
||||
res = RedditMin(content=CommentsPanel(link=link, listing=listing.listing(),
|
||||
res = RedditMin(content=CommentsPanel(link=link,
|
||||
listing=listing.listing(),
|
||||
expanded=auto_expand_panel(link),
|
||||
infobar=md_bar))
|
||||
|
||||
@@ -194,15 +186,9 @@ class ToolbarController(RedditController):
|
||||
link = utils.link_from_url(url, multiple = False)
|
||||
|
||||
if link:
|
||||
link_builder = IDBuilder((link._fullname,))
|
||||
|
||||
res = FrameToolbar(link = link_builder.get_items()[0][0],
|
||||
title = link.title,
|
||||
url = link.url,
|
||||
domain = None
|
||||
if link.is_self
|
||||
else domain(link.url),
|
||||
expanded = auto_expand_panel(link))
|
||||
link = list(wrap_links(link, wrapper = FrameToolbar))
|
||||
if link:
|
||||
res = link[0]
|
||||
else:
|
||||
res = FrameToolbar(link = None,
|
||||
title = None,
|
||||
|
||||
@@ -24,6 +24,7 @@ from __future__ import with_statement
|
||||
from r2.models import *
|
||||
from r2.lib.utils import sanitize_url, domain, randstr
|
||||
from r2.lib.strings import string_dict
|
||||
from r2.lib.pages.things import wrap_links
|
||||
|
||||
from pylons import g, c
|
||||
from pylons.i18n import _
|
||||
@@ -332,38 +333,14 @@ def find_preview_links(sr):
|
||||
return links
|
||||
|
||||
def rendered_link(links, media, compress):
|
||||
from pylons.controllers.util import abort
|
||||
from r2.controllers import ListingController
|
||||
|
||||
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 = ListingController.builder_wrapper)
|
||||
return LinkListing(b, nextprev=False,
|
||||
show_nums=True).listing().render(style='html')
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
with c.user.safe_set_attr:
|
||||
c.user.pref_compress = compress
|
||||
c.user.pref_media = media
|
||||
links = wrap_links(links, show_nums = True, num = 1)
|
||||
return links.render(style = "html")
|
||||
|
||||
def rendered_comment(comments):
|
||||
try:
|
||||
render_style = c.render_style
|
||||
|
||||
c.render_style = 'html'
|
||||
|
||||
b = IDBuilder([x._fullname for x in comments],
|
||||
num = 1)
|
||||
return LinkListing(b, nextprev=False,
|
||||
show_nums=False).listing().render(style='html')
|
||||
|
||||
finally:
|
||||
c.render_style = render_style
|
||||
return wrap_links(comments, num = 1).render(style = "html")
|
||||
|
||||
class BadImage(Exception): pass
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ from pylons import c
|
||||
import cgi
|
||||
import urllib
|
||||
import re
|
||||
from wrapped import Templated, CacheStub
|
||||
|
||||
SC_OFF = "<!-- SC_OFF -->"
|
||||
SC_ON = "<!-- SC_ON -->"
|
||||
@@ -75,6 +76,8 @@ class _Unsafe(unicode): pass
|
||||
def _force_unicode(text):
|
||||
try:
|
||||
text = unicode(text, 'utf-8')
|
||||
except UnicodeDecodeError:
|
||||
text = unicode(text, 'latin1')
|
||||
except TypeError:
|
||||
text = unicode(text)
|
||||
return text
|
||||
@@ -91,6 +94,12 @@ def websafe_json(text=""):
|
||||
def mako_websafe(text = ''):
|
||||
if text.__class__ == _Unsafe:
|
||||
return text
|
||||
elif isinstance(text, Templated):
|
||||
return _Unsafe(text.render())
|
||||
elif isinstance(text, CacheStub):
|
||||
return _Unsafe(text)
|
||||
elif text is None:
|
||||
return ""
|
||||
elif text.__class__ != unicode:
|
||||
text = _force_unicode(text)
|
||||
return c_websafe(text)
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
################################################################################
|
||||
from r2.lib.utils import tup
|
||||
from r2.lib.captcha import get_iden
|
||||
from r2.lib.wrapped import Wrapped
|
||||
from r2.lib.wrapped import Wrapped, StringTemplate
|
||||
from r2.lib.filters import websafe_json
|
||||
from r2.lib.template_helpers import replace_render
|
||||
from r2.lib.jsontemplates import get_api_subtype
|
||||
from r2.lib.base import BaseController
|
||||
from r2.lib.pages.things import wrap_links
|
||||
from r2.models import IDBuilder, Listing
|
||||
|
||||
import simplejson
|
||||
@@ -88,16 +88,11 @@ class JsonResponse(object):
|
||||
"""
|
||||
function for inserting/replacing things in listings.
|
||||
"""
|
||||
listing = None
|
||||
if isinstance(things, Listing):
|
||||
listing = things.listing()
|
||||
things = listing.things
|
||||
things = tup(things)
|
||||
if not all(isinstance(t, Wrapped) for t in things):
|
||||
wrap = kw.pop('wrap', Wrapped)
|
||||
b = IDBuilder([t._fullname for t in things], wrap)
|
||||
things = b.get_items()[0]
|
||||
data = [replace_render(listing, t) for t in things]
|
||||
things = wrap_links(things, wrapper = wrap)
|
||||
data = [t.render() for t in things]
|
||||
|
||||
if kw:
|
||||
for d in data:
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from utils import to36, tup, iters
|
||||
from wrapped import Wrapped
|
||||
from wrapped import Wrapped, StringTemplate, CacheStub, CachedVariable
|
||||
from mako.template import Template
|
||||
from r2.lib.filters import spaceCompress, safemarkdown
|
||||
import time, pytz
|
||||
@@ -42,11 +42,34 @@ def make_typename(typ):
|
||||
def make_fullname(typ, _id):
|
||||
return '%s_%s' % (make_typename(typ), to36(_id))
|
||||
|
||||
|
||||
class ObjectTemplate(StringTemplate):
|
||||
def __init__(self, d):
|
||||
self.d = d
|
||||
|
||||
def update(self, kw):
|
||||
def _update(obj):
|
||||
if isinstance(obj, (str, unicode)):
|
||||
return spaceCompress(StringTemplate(obj).finalize(kw))
|
||||
elif isinstance(obj, dict):
|
||||
return dict((k, _update(v)) for k, v in obj.iteritems())
|
||||
elif isinstance(obj, (list, tuple)):
|
||||
return map(_update, obj)
|
||||
elif isinstance(obj, CacheStub) and kw.has_key(obj.name):
|
||||
return kw[obj.name]
|
||||
else:
|
||||
return obj
|
||||
res = _update(self.d)
|
||||
return ObjectTemplate(res)
|
||||
|
||||
def finalize(self, kw = {}):
|
||||
return self.update(kw).d
|
||||
|
||||
class JsonTemplate(Template):
|
||||
def __init__(self): pass
|
||||
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return {}
|
||||
return ObjectTemplate({})
|
||||
|
||||
class TableRowTemplate(JsonTemplate):
|
||||
def cells(self, thing):
|
||||
@@ -59,15 +82,15 @@ class TableRowTemplate(JsonTemplate):
|
||||
return ""
|
||||
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return {"id": self.css_id(thing),
|
||||
"css_class": self.css_class(thing),
|
||||
"cells": self.cells(thing)}
|
||||
return ObjectTemplate(dict(id = self.css_id(thing),
|
||||
css_class = self.css_class(thing),
|
||||
cells = self.cells(thing)))
|
||||
|
||||
class UserItemJsonTemplate(TableRowTemplate):
|
||||
def cells(self, thing):
|
||||
cells = []
|
||||
for cell in thing.cells:
|
||||
r = Wrapped.part_render(thing, 'cell_type', cell)
|
||||
r = Templated.part_render(thing, 'cell_type', cell)
|
||||
cells.append(spaceCompress(r))
|
||||
return cells
|
||||
|
||||
@@ -90,18 +113,6 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
d.update(kw)
|
||||
return d
|
||||
|
||||
def points(self, wrapped):
|
||||
"""
|
||||
Generates the JS-style point triplet for votable elements
|
||||
(stored on the vl var on the JS side).
|
||||
"""
|
||||
score = wrapped.score
|
||||
likes = wrapped.likes
|
||||
base_score = score-1 if likes else score if likes is None else score+1
|
||||
base_score = [base_score + x for x in range(-1, 2)]
|
||||
return [wrapped.score_fmt(s) for s in base_score]
|
||||
|
||||
|
||||
def kind(self, wrapped):
|
||||
"""
|
||||
Returns a string literal which identifies the type of this
|
||||
@@ -122,31 +133,18 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
|
||||
* id : Thing _fullname of thing.
|
||||
* content : rendered representation of the thing by
|
||||
calling replace_render on it using the style of get_api_subtype().
|
||||
calling render on it using the style of get_api_subtype().
|
||||
"""
|
||||
from r2.lib.template_helpers import replace_render
|
||||
listing = thing.listing if hasattr(thing, "listing") else None
|
||||
return dict(id = thing._fullname,
|
||||
content = spaceCompress(
|
||||
replace_render(listing, thing,
|
||||
style=get_api_subtype())))
|
||||
|
||||
res = dict(id = thing._fullname,
|
||||
content = thing.render(style=get_api_subtype()))
|
||||
return res
|
||||
|
||||
def raw_data(self, thing):
|
||||
"""
|
||||
Complement to rendered_data. Called when a dictionary of
|
||||
thing data attributes is to be sent across the wire.
|
||||
"""
|
||||
def strip_data(x):
|
||||
if isinstance(x, dict):
|
||||
return dict((k, strip_data(v)) for k, v in x.iteritems())
|
||||
elif isinstance(x, iters):
|
||||
return [strip_data(y) for y in x]
|
||||
elif isinstance(x, Wrapped):
|
||||
return x.render()
|
||||
else:
|
||||
return x
|
||||
|
||||
return dict((k, strip_data(self.thing_attr(thing, v)))
|
||||
return dict((k, self.thing_attr(thing, v))
|
||||
for k, v in self._data_attrs_.iteritems())
|
||||
|
||||
def thing_attr(self, thing, attr):
|
||||
@@ -162,7 +160,9 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
return time.mktime(thing._date.timetuple())
|
||||
elif attr == "created_utc":
|
||||
return time.mktime(thing._date.astimezone(pytz.UTC).timetuple())
|
||||
return getattr(thing, attr) if hasattr(thing, attr) else None
|
||||
elif attr == "child":
|
||||
return CachedVariable("childlisting")
|
||||
return getattr(thing, attr, None)
|
||||
|
||||
def data(self, thing):
|
||||
if get_api_subtype():
|
||||
@@ -171,7 +171,8 @@ class ThingJsonTemplate(JsonTemplate):
|
||||
return self.raw_data(thing)
|
||||
|
||||
def render(self, thing = None, action = None, *a, **kw):
|
||||
return dict(kind = self.kind(thing), data = self.data(thing))
|
||||
return ObjectTemplate(dict(kind = self.kind(thing),
|
||||
data = self.data(thing)))
|
||||
|
||||
class SubredditJsonTemplate(ThingJsonTemplate):
|
||||
_data_attrs_ = ThingJsonTemplate.data_attrs(subscribers = "score",
|
||||
@@ -244,26 +245,17 @@ class CommentJsonTemplate(ThingJsonTemplate):
|
||||
return make_typename(Comment)
|
||||
|
||||
def rendered_data(self, wrapped):
|
||||
from r2.models import Comment, Link
|
||||
try:
|
||||
parent_id = wrapped.parent_id
|
||||
except AttributeError:
|
||||
parent_id = make_fullname(Link, wrapped.link_id)
|
||||
else:
|
||||
parent_id = make_fullname(Comment, parent_id)
|
||||
d = ThingJsonTemplate.rendered_data(self, wrapped)
|
||||
d['replies'] = self.thing_attr(wrapped, 'child')
|
||||
d['contentText'] = self.thing_attr(wrapped, 'body')
|
||||
d['contentHTML'] = self.thing_attr(wrapped, 'body_html')
|
||||
d['parent'] = parent_id
|
||||
d['link'] = make_fullname(Link, wrapped.link_id)
|
||||
d['link'] = self.thing_attr(wrapped, 'link_id')
|
||||
d['parent'] = self.thing_attr(wrapped, 'parent_id')
|
||||
return d
|
||||
|
||||
class MoreCommentJsonTemplate(CommentJsonTemplate):
|
||||
_data_attrs_ = dict(id = "_id36",
|
||||
name = "_fullname")
|
||||
def points(self, wrapped):
|
||||
return []
|
||||
|
||||
def kind(self, wrapped):
|
||||
return "more"
|
||||
|
||||
@@ -303,12 +295,14 @@ class MessageJsonTemplate(ThingJsonTemplate):
|
||||
parent_id = make_fullname(Message, parent_id)
|
||||
d = ThingJsonTemplate.rendered_data(self, wrapped)
|
||||
d['parent'] = parent_id
|
||||
d['contentText'] = self.thing_attr(wrapped, 'body')
|
||||
d['contentHTML'] = self.thing_attr(wrapped, 'body_html')
|
||||
return d
|
||||
|
||||
|
||||
class RedditJsonTemplate(JsonTemplate):
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return thing.content().render() if thing else {}
|
||||
return ObjectTemplate(thing.content().render() if thing else {})
|
||||
|
||||
class PanestackJsonTemplate(JsonTemplate):
|
||||
def render(self, thing = None, *a, **kw):
|
||||
@@ -316,36 +310,34 @@ class PanestackJsonTemplate(JsonTemplate):
|
||||
res = [x for x in res if x]
|
||||
if not res:
|
||||
return {}
|
||||
return res if len(res) > 1 else res[0]
|
||||
return ObjectTemplate(res if len(res) > 1 else res[0] )
|
||||
|
||||
class NullJsonTemplate(JsonTemplate):
|
||||
def render(self, thing = None, *a, **kw):
|
||||
return None
|
||||
return ""
|
||||
|
||||
class ListingJsonTemplate(ThingJsonTemplate):
|
||||
_data_attrs_ = dict(children = "things")
|
||||
|
||||
def points(self, w):
|
||||
return []
|
||||
def thing_attr(self, thing, attr):
|
||||
if attr == "things":
|
||||
res = []
|
||||
for a in thing.things:
|
||||
a.childlisting = False
|
||||
r = a.render()
|
||||
if isinstance(r, str):
|
||||
r = spaceCompress(r)
|
||||
res.append(r)
|
||||
return res
|
||||
return ThingJsonTemplate.thing_attr(self, thing, attr)
|
||||
|
||||
|
||||
def rendered_data(self, thing):
|
||||
from r2.lib.template_helpers import replace_render
|
||||
res = []
|
||||
for a in thing.things:
|
||||
a.listing = thing
|
||||
r = replace_render(thing, a, style = 'api')
|
||||
if isinstance(r, str):
|
||||
r = spaceCompress(r)
|
||||
res.append(r)
|
||||
return res
|
||||
return self.thing_attr(thing, "things")
|
||||
|
||||
def kind(self, wrapped):
|
||||
return "Listing"
|
||||
|
||||
def render(self, *a, **kw):
|
||||
res = ThingJsonTemplate.render(self, *a, **kw)
|
||||
return res
|
||||
|
||||
class OrganicListingJsonTemplate(ListingJsonTemplate):
|
||||
def kind(self, wrapped):
|
||||
return "OrganicListing"
|
||||
@@ -357,4 +349,4 @@ class TrafficJsonTemplate(JsonTemplate):
|
||||
if hasattr(thing, ival + "_data"):
|
||||
res[ival] = [[time.mktime(date.timetuple())] + list(data)
|
||||
for date, data in getattr(thing, ival+"_data")]
|
||||
return res
|
||||
return ObjectTemplate(res)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
import pylons
|
||||
import pylons, sha
|
||||
from mako.template import Template as mTemplate
|
||||
from mako.exceptions import TemplateLookupException
|
||||
from r2.lib.filters import websafe, unsafe
|
||||
@@ -67,6 +67,11 @@ class tp_manager:
|
||||
template = _loader.load_template(self.templates[key])
|
||||
if cache:
|
||||
self.templates[key] = template
|
||||
# also store a hash for the template
|
||||
if (not hasattr(template, "hash") and
|
||||
hasattr(template, "filename")):
|
||||
with open(template.filename, 'r') as handle:
|
||||
template.hash = sha.new(handle.read()).hexdigest()
|
||||
# cache also for the base class so
|
||||
# introspection is not required on subsequent passes
|
||||
if key != top_key:
|
||||
@@ -78,3 +83,4 @@ class tp_manager:
|
||||
if not template or not isinstance(template, self.Template):
|
||||
raise AttributeError, ("template doesn't exist for %s" % str(top_key))
|
||||
return template
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from wrapped import Wrapped, Styled
|
||||
from wrapped import CachedTemplate, Styled
|
||||
from pylons import c, request, g
|
||||
from utils import query_string, timeago
|
||||
from strings import StringHandler, plurals
|
||||
@@ -29,6 +29,7 @@ from r2.lib.filters import _force_unicode
|
||||
from pylons.i18n import _
|
||||
|
||||
|
||||
|
||||
class MenuHandler(StringHandler):
|
||||
"""Bastard child of StringHandler and plurals. Menus are
|
||||
typically a single word (and in some cases, a single plural word
|
||||
@@ -172,7 +173,7 @@ class NavMenu(Styled):
|
||||
'style' parameter sets what template/layout to use to differentiate, say,
|
||||
a dropdown from a flatlist, while the optional _class, and _id attributes
|
||||
can be used to set individualized CSS."""
|
||||
|
||||
|
||||
def __init__(self, options, default = None, title = '', type = "dropdown",
|
||||
base_path = '', separator = '|', **kw):
|
||||
self.options = options
|
||||
@@ -208,9 +209,6 @@ class NavMenu(Styled):
|
||||
if opt.dest == self.default:
|
||||
return opt
|
||||
|
||||
def __repr__(self):
|
||||
return "<NavMenu>"
|
||||
|
||||
def __iter__(self):
|
||||
for opt in self.options:
|
||||
yield opt
|
||||
@@ -223,14 +221,17 @@ class NavButton(Styled):
|
||||
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
|
||||
aliases = set(a.rstrip('/') for a in aliases)
|
||||
aliases.add(dest.rstrip('/'))
|
||||
|
||||
self.request_params = dict(request.params)
|
||||
self.stripped_path = request.path.rstrip('/').lower()
|
||||
|
||||
Styled.__init__(self, style = style, sr_path = sr_path,
|
||||
nocname = nocname, target = target,
|
||||
nocname = nocname, target = target,
|
||||
aliases = aliases, dest = dest,
|
||||
selected = False,
|
||||
title = title, opt = opt, **kw)
|
||||
|
||||
def build(self, base_path = ''):
|
||||
@@ -239,7 +240,7 @@ class NavButton(Styled):
|
||||
# append to the path or update the get params dependent on presence
|
||||
# of opt
|
||||
if self.opt:
|
||||
p = request.get.copy()
|
||||
p = self.request_params.copy()
|
||||
p[self.opt] = self.dest
|
||||
else:
|
||||
p = {}
|
||||
@@ -258,13 +259,11 @@ class NavButton(Styled):
|
||||
def is_selected(self):
|
||||
"""Given the current request path, would the button be selected."""
|
||||
if self.opt:
|
||||
return request.params.get(self.opt, '') in self.aliases
|
||||
return self.request_params.get(self.opt, '') in self.aliases
|
||||
else:
|
||||
stripped_path = request.path.rstrip('/').lower()
|
||||
ustripped_path = _force_unicode(stripped_path)
|
||||
if stripped_path == self.bare_path:
|
||||
if self.stripped_path == self.bare_path:
|
||||
return True
|
||||
if stripped_path in self.aliases:
|
||||
if self.stripped_path in self.aliases:
|
||||
return True
|
||||
|
||||
def selected_title(self):
|
||||
@@ -277,17 +276,24 @@ class OffsiteButton(NavButton):
|
||||
self.sr_path = False
|
||||
self.path = self.bare_path = self.dest
|
||||
|
||||
def cachable_attrs(self):
|
||||
return [('path', self.path), ('title', self.title)]
|
||||
|
||||
class SubredditButton(NavButton):
|
||||
def __init__(self, sr):
|
||||
self.sr = sr
|
||||
NavButton.__init__(self, sr.name, sr.path, False)
|
||||
self.path = sr.path
|
||||
NavButton.__init__(self, sr.name, sr.path, False,
|
||||
isselected = (c.site == sr))
|
||||
|
||||
def build(self, base_path = ''):
|
||||
self.path = self.sr.path
|
||||
pass
|
||||
|
||||
def is_selected(self):
|
||||
return c.site == self.sr
|
||||
return self.isselected
|
||||
|
||||
def cachable_attrs(self):
|
||||
return [('path', self.path), ('title', self.title),
|
||||
('isselected', self.isselected)]
|
||||
|
||||
class NamedButton(NavButton):
|
||||
"""Convenience class for handling the majority of NavButtons
|
||||
@@ -345,13 +351,11 @@ class SimpleGetMenu(NavMenu):
|
||||
type = 'lightdrop'
|
||||
|
||||
def __init__(self, **kw):
|
||||
kw['default'] = kw.get('default', self.default)
|
||||
kw['base_path'] = kw.get('base_path') or request.path
|
||||
buttons = [NavButton(self.make_title(n), n, opt = self.get_param)
|
||||
for n in self.options]
|
||||
kw['default'] = kw.get('default', self.default)
|
||||
kw['base_path'] = kw.get('base_path') or request.path
|
||||
NavMenu.__init__(self, buttons, type = self.type, **kw)
|
||||
#if kw.get('default'):
|
||||
# self.selected = kw['default']
|
||||
|
||||
def make_title(self, attr):
|
||||
return menu[attr]
|
||||
@@ -467,29 +471,29 @@ class NumCommentsMenu(SimpleGetMenu):
|
||||
|
||||
def __init__(self, num_comments, **context):
|
||||
self.num_comments = num_comments
|
||||
self.max_comments = g.max_comments
|
||||
self.user_num = c.user.pref_num_comments
|
||||
SimpleGetMenu.__init__(self, **context)
|
||||
|
||||
def make_title(self, attr):
|
||||
user_num = c.user.pref_num_comments
|
||||
if user_num > self.num_comments:
|
||||
if self.user_num > self.num_comments:
|
||||
# no menus needed if the number of comments is smaller
|
||||
# than any of the limits
|
||||
return ""
|
||||
elif self.num_comments > g.max_comments:
|
||||
elif self.num_comments > self.max_comments:
|
||||
# if the number present is larger than the global max,
|
||||
# label the menu as the user pref and the max number
|
||||
return dict(true=str(g.max_comments),
|
||||
false=str(user_num))[attr]
|
||||
return dict(true=str(self.max_comments),
|
||||
false=str(self.user_num))[attr]
|
||||
else:
|
||||
# if the number is less than the global max, display "all"
|
||||
# instead for the upper bound.
|
||||
return dict(true=_("all"),
|
||||
false=str(user_num))[attr]
|
||||
false=str(self.user_num))[attr]
|
||||
|
||||
|
||||
def render(self, **kw):
|
||||
user_num = c.user.pref_num_comments
|
||||
if user_num > self.num_comments:
|
||||
if self.user_num > self.num_comments:
|
||||
return ""
|
||||
return SimpleGetMenu.render(self, **kw)
|
||||
|
||||
|
||||
@@ -20,18 +20,18 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from pylons import c, g
|
||||
from r2.lib.wrapped import Wrapped
|
||||
from r2.lib.wrapped import Templated
|
||||
from pages import Reddit
|
||||
from r2.lib.menus import NamedButton, NavButton, menu, NavMenu
|
||||
|
||||
class AdminSidebar(Wrapped):
|
||||
class AdminSidebar(Templated):
|
||||
def __init__(self, user):
|
||||
self.user = user
|
||||
|
||||
|
||||
class Details(Wrapped):
|
||||
class Details(Templated):
|
||||
def __init__(self, link):
|
||||
Wrapped.__init__(self)
|
||||
Templated.__init__(self)
|
||||
self.link = link
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ from __future__ import with_statement
|
||||
import os, re, sys, socket, time, random, time, signal
|
||||
from itertools import chain
|
||||
|
||||
from wrapped import Wrapped
|
||||
from wrapped import Templated
|
||||
from datetime import datetime, timedelta
|
||||
from pylons import g
|
||||
from r2.lib.utils import tup
|
||||
@@ -58,7 +58,7 @@ class ShellProcess(object):
|
||||
return self.output
|
||||
|
||||
|
||||
class AppServiceMonitor(Wrapped):
|
||||
class AppServiceMonitor(Templated):
|
||||
cache_key = "service_datalogger_data_"
|
||||
cache_key_small = "service_datalogger_db_summary_"
|
||||
cache_lifetime = "memcached_lifetime"
|
||||
@@ -99,7 +99,7 @@ class AppServiceMonitor(Wrapped):
|
||||
|
||||
self._db_info = db_info
|
||||
self.hostlogs = []
|
||||
Wrapped.__init__(self)
|
||||
Templated.__init__(self)
|
||||
|
||||
@classmethod
|
||||
def set_cache_lifetime(cls, data):
|
||||
@@ -145,7 +145,7 @@ class AppServiceMonitor(Wrapped):
|
||||
|
||||
def render(self, *a, **kw):
|
||||
self.hostlogs = list(self)
|
||||
return Wrapped.render(self, *a, **kw)
|
||||
return Templated.render(self, *a, **kw)
|
||||
|
||||
def monitor(self, srvname, loop = True, loop_time = 5, *a, **kw):
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ class Score(object):
|
||||
fasion, used primarily by the score() method in printable.html"""
|
||||
@staticmethod
|
||||
def number_only(x):
|
||||
return max(x, 0)
|
||||
return str(max(x, 0))
|
||||
|
||||
@staticmethod
|
||||
def points(x):
|
||||
|
||||
@@ -20,16 +20,16 @@
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from r2.models import *
|
||||
from r2.lib.jsontemplates import is_api
|
||||
from filters import unsafe, websafe
|
||||
from r2.lib.utils import vote_hash, UrlParser
|
||||
from r2.lib.utils import vote_hash, UrlParser, timesince
|
||||
|
||||
from mako.filters import url_escape
|
||||
import simplejson
|
||||
import os.path
|
||||
from copy import copy
|
||||
import random
|
||||
from pylons import i18n, g, c
|
||||
from pylons import g, c
|
||||
from pylons.i18n import _, ungettext
|
||||
|
||||
def static(file):
|
||||
"""
|
||||
@@ -76,7 +76,6 @@ def class_dict():
|
||||
res = ', '.join(classes)
|
||||
return unsafe('{ %s }' % res)
|
||||
|
||||
|
||||
def path_info():
|
||||
loc = dict(path = request.path,
|
||||
params = dict(request.get))
|
||||
@@ -84,82 +83,86 @@ def path_info():
|
||||
return unsafe(simplejson.dumps(loc))
|
||||
|
||||
|
||||
def replace_render(listing, item, style = None, display = True):
|
||||
style = style or c.render_style or 'html'
|
||||
rendered_item = item.render(style = style)
|
||||
|
||||
# for string rendered items
|
||||
def string_replace(x, y):
|
||||
return rendered_item.replace(x, y)
|
||||
|
||||
# for JSON responses
|
||||
def dict_replace(x, y):
|
||||
try:
|
||||
res = rendered_item['data']['content']
|
||||
rendered_item['data']['content'] = res.replace(x, y)
|
||||
except AttributeError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
return rendered_item
|
||||
|
||||
if is_api():
|
||||
child_txt = ""
|
||||
else:
|
||||
child_txt = ( hasattr(item, "child") and item.child )\
|
||||
and item.child.render(style = style) or ""
|
||||
|
||||
# handle API calls differently from normal request: dicts not strings are passed around
|
||||
if isinstance(rendered_item, dict):
|
||||
replace_fn = dict_replace
|
||||
try:
|
||||
rendered_item['data']['child'] = child_txt
|
||||
except AttributeError:
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
replace_fn = string_replace
|
||||
rendered_item = replace_fn(u"$child", child_txt)
|
||||
|
||||
#only LinkListing has a show_nums attribute
|
||||
if listing:
|
||||
if hasattr(listing, "show_nums"):
|
||||
if listing.show_nums:
|
||||
num_str = str(item.num)
|
||||
if hasattr(listing, "num_margin"):
|
||||
num_margin = listing.num_margin
|
||||
else:
|
||||
num_margin = "%.2fex" % (len(str(listing.max_num))*1.1)
|
||||
else:
|
||||
num_str = ''
|
||||
num_margin = "0px"
|
||||
def replace_render(listing, item, render_func):
|
||||
def _replace_render(style = None, display = True):
|
||||
"""
|
||||
A helper function for listings to set uncachable attributes on a
|
||||
rendered thing (item) to its proper display values for the current
|
||||
context.
|
||||
"""
|
||||
style = style or c.render_style or 'html'
|
||||
replacements = {}
|
||||
|
||||
rendered_item = replace_fn(u"$numcolmargin", num_margin)
|
||||
rendered_item = replace_fn(u"$num", num_str)
|
||||
|
||||
if hasattr(listing, "max_score"):
|
||||
mid_margin = len(str(listing.max_score))
|
||||
if hasattr(listing, "mid_margin"):
|
||||
mid_margin = listing.mid_margin
|
||||
elif mid_margin == 1:
|
||||
mid_margin = "15px"
|
||||
child_txt = ( hasattr(item, "child") and item.child )\
|
||||
and item.child.render(style = style) or ""
|
||||
replacements["childlisting"] = child_txt
|
||||
|
||||
|
||||
#only LinkListing has a show_nums attribute
|
||||
if listing:
|
||||
if hasattr(listing, "show_nums"):
|
||||
if listing.show_nums:
|
||||
num_str = str(item.num)
|
||||
if hasattr(listing, "num_margin"):
|
||||
num_margin = str(listing.num_margin)
|
||||
else:
|
||||
num_margin = "%.2fex" % (len(str(listing.max_num))*1.1)
|
||||
else:
|
||||
num_str = ''
|
||||
num_margin = "0px"
|
||||
|
||||
replacements["numcolmargin"] = num_margin
|
||||
replacements["num"] = num_str
|
||||
|
||||
if hasattr(listing, "max_score"):
|
||||
mid_margin = len(str(listing.max_score))
|
||||
if hasattr(listing, "mid_margin"):
|
||||
mid_margin = str(listing.mid_margin)
|
||||
elif mid_margin == 1:
|
||||
mid_margin = "15px"
|
||||
else:
|
||||
mid_margin = "%dex" % (mid_margin+1)
|
||||
|
||||
replacements["midcolmargin"] = mid_margin
|
||||
|
||||
#$votehash is only present when voting arrows are present
|
||||
if c.user_is_loggedin:
|
||||
replacements['votehash'] = vote_hash(c.user, item,
|
||||
listing.vote_hash_type)
|
||||
if hasattr(item, "num_comments"):
|
||||
if not item.num_comments:
|
||||
# generates "comment" the imperative verb
|
||||
com_label = _("comment {verb}")
|
||||
com_cls = 'comments empty'
|
||||
else:
|
||||
mid_margin = "%dex" % (mid_margin+1)
|
||||
# generates "XX comments" as a noun
|
||||
com_label = ungettext("comment", "comments", item.num_comments)
|
||||
com_label = strings.number_label % dict(num=item.num_comments,
|
||||
thing=com_label)
|
||||
com_cls = 'comments'
|
||||
replacements['numcomments'] = com_label
|
||||
replacements['commentcls'] = com_cls
|
||||
|
||||
replacements['display'] = "" if display else "style='display:none'"
|
||||
|
||||
if hasattr(item, "render_score"):
|
||||
# replace the score stub
|
||||
(replacements['scoredislikes'],
|
||||
replacements['scoreunvoted'],
|
||||
replacements['scorelikes']) = item.render_score
|
||||
|
||||
# compute the timesince here so we don't end up caching it
|
||||
if hasattr(item, "_date"):
|
||||
replacements['timesince'] = timesince(item._date)
|
||||
|
||||
rendered_item = replace_fn(u"$midcolmargin", mid_margin)
|
||||
|
||||
# TODO: one of these things is not like the other. We should & ->
|
||||
# $ elsewhere as it plays nicer with the websafe filter.
|
||||
rendered_item = replace_fn(u"$ListClass", listing._js_cls)
|
||||
|
||||
#$votehash is only present when voting arrows are present
|
||||
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
|
||||
renderer = render_func or item.render
|
||||
res = renderer(style = style, **replacements)
|
||||
if isinstance(res, (str, unicode)):
|
||||
return unsafe(res)
|
||||
return res
|
||||
|
||||
return _replace_render
|
||||
|
||||
def get_domain(cname = False, subreddit = True, no_www = False):
|
||||
"""
|
||||
@@ -181,15 +184,19 @@ def get_domain(cname = False, subreddit = True, no_www = False):
|
||||
the trailing path).
|
||||
|
||||
"""
|
||||
# locally cache these lookups as this gets run in a loop in add_props
|
||||
domain = g.domain
|
||||
if not no_www and g.domain_prefix:
|
||||
domain = g.domain_prefix + "." + g.domain
|
||||
if cname and c.cname and c.site.domain:
|
||||
domain = c.site.domain
|
||||
domain_prefix = g.domain_prefix
|
||||
site = c.site
|
||||
ccname = c.cname
|
||||
if not no_www and domain_prefix:
|
||||
domain = domain_prefix + "." + domain
|
||||
if cname and ccname and site.domain:
|
||||
domain = site.domain
|
||||
if hasattr(request, "port") and request.port:
|
||||
domain += ":" + str(request.port)
|
||||
if (not c.cname or not cname) and subreddit:
|
||||
domain += c.site.path.rstrip('/')
|
||||
if (not ccname or not cname) and subreddit:
|
||||
domain += site.path.rstrip('/')
|
||||
return domain
|
||||
|
||||
def dockletStr(context, type, browser):
|
||||
@@ -241,6 +248,9 @@ def add_sr(path, sr_path = True, nocname=False, force_hostname = False):
|
||||
|
||||
* sr_path: if a cname is not used for the domain, updates the
|
||||
path to include c.site.path.
|
||||
|
||||
For caching purposes: note that this function uses:
|
||||
c.cname, c.render_style, c.site.name
|
||||
"""
|
||||
# don't do anything if it is just an anchor
|
||||
if path.startswith('#') or path.startswith('javascript:'):
|
||||
@@ -289,7 +299,7 @@ def choose_width(link, width):
|
||||
if width:
|
||||
return width - 5
|
||||
else:
|
||||
if link:
|
||||
if hasattr(link, "_ups"):
|
||||
return 100 + (10 * (len(str(link._ups - link._downs))))
|
||||
else:
|
||||
return 110
|
||||
|
||||
@@ -27,7 +27,7 @@ from pylons.i18n import _
|
||||
from babel import Locale
|
||||
import os, re
|
||||
import cPickle as pickle
|
||||
from wrapped import Wrapped
|
||||
from wrapped import Templated
|
||||
from utils import Storage
|
||||
from md5 import md5
|
||||
|
||||
@@ -98,7 +98,7 @@ def hax(string):
|
||||
return hax_dict.get(string, string)
|
||||
|
||||
|
||||
class TranslatedString(Wrapped):
|
||||
class TranslatedString(Templated):
|
||||
class _SubstString:
|
||||
def __init__(self, string, enabled = True):
|
||||
self.str = hax(string)
|
||||
@@ -157,7 +157,7 @@ class TranslatedString(Wrapped):
|
||||
|
||||
def __init__(self, translator, sing, plural = '', message = '',
|
||||
enabled = True, locale = '', tip = '', index = 0):
|
||||
Wrapped.__init__(self)
|
||||
Templated.__init__(self)
|
||||
|
||||
self.translator = translator
|
||||
self.message = message
|
||||
|
||||
@@ -19,29 +19,428 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from filters import unsafe
|
||||
from utils import storage
|
||||
|
||||
from itertools import chain
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(500)
|
||||
from datetime import datetime
|
||||
import re, types
|
||||
|
||||
class NoTemplateFound(Exception): pass
|
||||
|
||||
class Wrapped(object):
|
||||
class StringTemplate(object):
|
||||
"""
|
||||
Simple-minded string templating, where variables of the for $____
|
||||
in a strinf are replaced with values based on a dictionary.
|
||||
|
||||
Unline the built-in Template class, this supports an update method
|
||||
|
||||
We could use the built in python Template class for this, but
|
||||
unfortunately it doesn't handle unicode as gracefully as we'd
|
||||
like.
|
||||
"""
|
||||
start_delim = "<$>"
|
||||
end_delim = "</$>"
|
||||
pattern2 = r"[_a-z][_a-z0-9]*"
|
||||
pattern2 = r"%(start_delim)s(?:(?P<named>%(pattern)s))%(end_delim)s" % \
|
||||
dict(pattern = pattern2,
|
||||
start_delim = re.escape(start_delim),
|
||||
end_delim = re.escape(end_delim),
|
||||
)
|
||||
pattern2 = re.compile(pattern2, re.UNICODE)
|
||||
|
||||
def __init__(self, template):
|
||||
# for the nth time, we have to transform the string into
|
||||
# unicode. Otherwise, re.sub will choke on non-ascii
|
||||
# characters.
|
||||
try:
|
||||
self.template = unicode(template)
|
||||
except UnicodeDecodeError:
|
||||
self.template = unicode(template, "utf8")
|
||||
|
||||
def __init__(self, *lookups, **context):
|
||||
self.lookups = lookups
|
||||
def update(self, d):
|
||||
"""
|
||||
Given a dictionary of replacement rules for the Template,
|
||||
replace variables in the template (once!) and return an
|
||||
updated Template.
|
||||
"""
|
||||
if d:
|
||||
def convert(m):
|
||||
name = m.group("named")
|
||||
return d.get(name, self.start_delim + name + self.end_delim)
|
||||
return self.__class__(self.pattern2.sub(convert, self.template))
|
||||
return self
|
||||
|
||||
def finalize(self, d = {}):
|
||||
"""
|
||||
The same as update, except the dictionary is optional and the
|
||||
object returned will be a unicode object.
|
||||
"""
|
||||
return self.update(d).template
|
||||
|
||||
|
||||
class CacheStub(object):
|
||||
"""
|
||||
When using cached renderings, this class generates a stub based on
|
||||
the hash of the Templated item passed into init for the style
|
||||
specified.
|
||||
|
||||
This class is suitable as a stub object (in the case of API calls)
|
||||
and wil render in a string form suitable for replacement with
|
||||
StringTemplate in the case of normal rendering.
|
||||
"""
|
||||
def __init__(self, item, style):
|
||||
self.name = "h%s%s" % (id(item), str(style).replace('-', '_'))
|
||||
|
||||
def __str__(self):
|
||||
return StringTemplate.start_delim + self.name + \
|
||||
StringTemplate.end_delim
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s: %s>" % (self.__class__.__name__, self.name)
|
||||
|
||||
class CachedVariable(CacheStub):
|
||||
"""
|
||||
Same use as CacheStubs in normal templates, except it can be
|
||||
applied to where we would normally put a '$____' variable by hand
|
||||
in a template (file).
|
||||
"""
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
||||
class Templated(object):
|
||||
"""
|
||||
Replaces the Wrapped class (which has now a subclass and which
|
||||
takes an thing to be wrapped).
|
||||
|
||||
Templated objects are suitable for rendering and caching, with a
|
||||
render loop desgined to fetch other cached templates and insert
|
||||
them into the current template.
|
||||
|
||||
"""
|
||||
|
||||
# is this template cachable (see CachedTemplate)
|
||||
cachable = False
|
||||
# attributes that will not be made into the cache key
|
||||
cache_ignore = set()
|
||||
|
||||
def __repr__(self):
|
||||
return "<Templated: %s>" % self.__class__.__name__
|
||||
|
||||
def __init__(self, **context):
|
||||
"""
|
||||
uses context to init __dict__ (making this object a bit like a storage)
|
||||
"""
|
||||
for k, v in context.iteritems():
|
||||
setattr(self, k, v)
|
||||
if not hasattr(self, "render_class"):
|
||||
self.render_class = self.__class__
|
||||
|
||||
def template(self, style = 'html'):
|
||||
"""
|
||||
Fetches template from the template manager
|
||||
"""
|
||||
from r2.config.templates import tpm
|
||||
from pylons import g
|
||||
debug = g.template_debug
|
||||
template = None
|
||||
try:
|
||||
template = tpm.get(self.render_class,
|
||||
style, cache = not debug)
|
||||
except AttributeError:
|
||||
raise NoTemplateFound, (repr(self), style)
|
||||
|
||||
return template
|
||||
|
||||
def cache_key(self, *a):
|
||||
"""
|
||||
if cachable, this function is used to generate the cache key.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def render_nocache(self, attr, style):
|
||||
"""
|
||||
No-frills (or caching) rendering of the template. The
|
||||
template is returned as a subclass of StringTemplate and
|
||||
therefore finalize() must be called on it to turn it into its
|
||||
final form
|
||||
"""
|
||||
from filters import unsafe
|
||||
from pylons import c
|
||||
# the style has to default to the global render style
|
||||
# fetch template
|
||||
template = self.template(style)
|
||||
if template:
|
||||
# store the global render style (since child templates
|
||||
render_style = c.render_style
|
||||
c.render_style = style
|
||||
# are we doing a partial render?
|
||||
if attr:
|
||||
template = template.get_def(attr)
|
||||
# render the template
|
||||
res = template.render(thing = self)
|
||||
if not isinstance(res, StringTemplate):
|
||||
res = StringTemplate(res)
|
||||
# reset the global render style
|
||||
c.render_style = render_style
|
||||
return res
|
||||
else:
|
||||
raise NoTemplateFound, repr(self)
|
||||
|
||||
def _render(self, attr, style, **kwargs):
|
||||
"""
|
||||
Renders the current template with the current style, possibly
|
||||
doing a part_render if attr is not None.
|
||||
|
||||
if this is the first template to be rendered, it is will track
|
||||
cachable templates, insert stubs for them in the output,
|
||||
get_multi from the cache, and render the uncached templates.
|
||||
Uncached but cachable templates are inserted back into the
|
||||
cache with a set_multi.
|
||||
|
||||
NOTE: one of the interesting issues with this function is that
|
||||
on each newly rendered thing, it is possible that that
|
||||
rendering has in turn cause more cachable things to be
|
||||
fetched. Thus the first template to be rendered runs a loop
|
||||
and keeps rendering until there is nothing left to render.
|
||||
Then it updates the master template until it doesn't change.
|
||||
|
||||
NOTE 2: anything passed in as a kw to render (and thus
|
||||
_render) will not be part of the cached version of the object,
|
||||
and will substituted last.
|
||||
"""
|
||||
from pylons import c, g
|
||||
style = style or c.render_style or 'html'
|
||||
|
||||
# prepare (and store) the list of cachable items.
|
||||
primary = False
|
||||
if not isinstance(c.render_tracker, dict):
|
||||
primary = True
|
||||
c.render_tracker = {}
|
||||
|
||||
# insert a stub for cachable non-primary templates
|
||||
if self.cachable:
|
||||
res = CacheStub(self, style)
|
||||
cache_key = self.cache_key(attr, style)
|
||||
# in the tracker, we need to store:
|
||||
# The render cache key (res.name)
|
||||
# The memcached cache key(cache_key)
|
||||
# who I am (self) and what am I doing (attr, style) with what
|
||||
# (kwargs)
|
||||
c.render_tracker[res.name] = (cache_key, (self,
|
||||
(attr, style, kwargs)))
|
||||
else:
|
||||
# either a primary template or not cachable, so render it
|
||||
res = self.render_nocache(attr, style)
|
||||
|
||||
# if this is the primary template, let the caching games begin
|
||||
if primary:
|
||||
# updates will be the (self-updated) list of all of
|
||||
# the cached templates that have been cached or
|
||||
# rendered.
|
||||
updates = {}
|
||||
# to_cache is just the keys of the cached templates
|
||||
# that were not in the cache.
|
||||
to_cache = set([])
|
||||
while c.render_tracker:
|
||||
# copy and wipe the tracker. It'll get repopulated if
|
||||
# any of the subsequent render()s call cached objects.
|
||||
current = c.render_tracker
|
||||
c.render_tracker = {}
|
||||
|
||||
# do a multi-get. NOTE: cache keys are the first item
|
||||
# in the tuple that is the current dict's values.
|
||||
# This dict cast will generate a new dict of cache_key
|
||||
# to value
|
||||
cached = g.rendercache.get_multi(dict(current.values()))
|
||||
# replacements will be a map of key -> rendered content
|
||||
# for updateing the current set of updates
|
||||
replacements = {}
|
||||
|
||||
new_updates = {}
|
||||
# render items that didn't make it into the cached list
|
||||
for key, (cache_key, others) in current.iteritems():
|
||||
# unbundle the remaining args
|
||||
item, (attr, style, kw) = others
|
||||
if cache_key not in cached:
|
||||
# this had to be rendered, so cache it later
|
||||
to_cache.add(cache_key)
|
||||
# render the item and apply the stored kw args
|
||||
r = item.render_nocache(attr, style)
|
||||
else:
|
||||
r = cached[cache_key]
|
||||
# store the unevaluated templates in
|
||||
# cached for caching
|
||||
replacements[key] = r.finalize(kw)
|
||||
new_updates[key] = (cache_key, (r, kw))
|
||||
|
||||
# update the updates so that when we can do the
|
||||
# replacement in one pass.
|
||||
|
||||
# NOTE: keep kw, but don't update based on them.
|
||||
# We might have to cache these later, and we want
|
||||
# to have things like $child present.
|
||||
for k in updates.keys():
|
||||
cache_key, (value, kw) = updates[k]
|
||||
value = value.update(replacements)
|
||||
updates[k] = cache_key, (value, kw)
|
||||
|
||||
updates.update(new_updates)
|
||||
|
||||
# at this point, we haven't touched res, but updates now
|
||||
# has the list of all the updates we could conceivably
|
||||
# want to make, and to_cache is the list of cache keys
|
||||
# that we didn't find in the cache.
|
||||
|
||||
# cache content that was newly rendered
|
||||
g.rendercache.set_multi(dict((k, v)
|
||||
for k, (v, kw) in updates.values()
|
||||
if k in to_cache))
|
||||
|
||||
# edge case: this may be the primary tempalte and cachable
|
||||
if isinstance(res, CacheStub):
|
||||
res = updates[res.name][1][0]
|
||||
|
||||
# now we can update the updates to make use of their kw args.
|
||||
updates = dict((k, v.finalize(kw))
|
||||
for k, (foo, (v, kw)) in updates.iteritems())
|
||||
|
||||
# update the response to use these values
|
||||
# replace till we can't replace any more.
|
||||
npasses = 0
|
||||
while True:
|
||||
npasses += 1
|
||||
r = res
|
||||
res = res.update(kwargs).update(updates)
|
||||
semi_final = res.finalize()
|
||||
if r.finalize() == res.finalize():
|
||||
res = semi_final
|
||||
break
|
||||
|
||||
# wipe out the render tracker object
|
||||
c.render_tracker = None
|
||||
elif not isinstance(res, CacheStub):
|
||||
# we're done. Update the template based on the args passed in
|
||||
res = res.finalize(kwargs)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def render(self, style = None, **kw):
|
||||
from r2.lib.filters import unsafe
|
||||
res = self._render(None, style, **kw)
|
||||
return unsafe(res) if isinstance(res, str) else res
|
||||
|
||||
def part_render(self, attr, **kw):
|
||||
style = kw.get('style')
|
||||
if style: del kw['style']
|
||||
return self._render(attr, style, **kw)
|
||||
|
||||
|
||||
class Uncachable(Exception): pass
|
||||
|
||||
_easy_cache_cls = set([bool, int, long, float, unicode, str, types.NoneType,
|
||||
datetime])
|
||||
def make_cachable(v, *a):
|
||||
"""
|
||||
Given an arbitrary object,
|
||||
"""
|
||||
if v.__class__ in _easy_cache_cls or isinstance(v, type):
|
||||
try:
|
||||
return unicode(v)
|
||||
except UnicodeDecodeError:
|
||||
try:
|
||||
return unicode(v, "utf8")
|
||||
except (TypeError, UnicodeDecodeError):
|
||||
return repr(v)
|
||||
elif isinstance(v, (types.MethodType, CachedVariable) ):
|
||||
return
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
return repr([make_cachable(x, *a) for x in v])
|
||||
elif isinstance(v, dict):
|
||||
return repr(dict((k, make_cachable(v[k], *a))
|
||||
for k in sorted(v.iterkeys())))
|
||||
elif hasattr(v, "cache_key"):
|
||||
return v.cache_key(*a)
|
||||
else:
|
||||
raise Uncachable, type(v)
|
||||
|
||||
class CachedTemplate(Templated):
|
||||
cachable = True
|
||||
|
||||
def cachable_attrs(self):
|
||||
"""
|
||||
Generates an iterator of attr names and their values for every
|
||||
attr on this element that should be used in generating the cache key.
|
||||
"""
|
||||
return ((k, self.__dict__[k]) for k in sorted(self.__dict__)
|
||||
if (k not in self.cache_ignore and not k.startswith('_')))
|
||||
|
||||
def cache_key(self, attr, style, *a):
|
||||
from pylons import c
|
||||
|
||||
# if template debugging is on, there will be no hash and we
|
||||
# can make the caching process-local.
|
||||
template_hash = getattr(self.template(style), "hash",
|
||||
id(self.__class__))
|
||||
|
||||
# these values are needed to render any link on the site, and
|
||||
# a menu is just a set of links, so we best cache against
|
||||
# them.
|
||||
keys = [c.user_is_loggedin, c.use_is_admin,
|
||||
c.render_style, c.cname, c.lang, c.site.name,
|
||||
template_hash]
|
||||
keys = [make_cachable(x, *a) for x in keys]
|
||||
|
||||
# add all parameters sent into __init__, using their current value
|
||||
auto_keys = [(k, make_cachable(v, attr, style, *a))
|
||||
for k, v in self.cachable_attrs()]
|
||||
|
||||
# lastly, add anything else that was passed in.
|
||||
keys.append(repr(auto_keys))
|
||||
keys.extend(make_cachable(x) for x in a)
|
||||
|
||||
return "<%s:[%s]>" % (self.__class__.__name__, u''.join(keys))
|
||||
|
||||
|
||||
class Wrapped(CachedTemplate):
|
||||
# default to false, evaluate
|
||||
cachable = False
|
||||
cache_ignore = set(['lookups'])
|
||||
|
||||
def cache_key(self, attr, style):
|
||||
if self.cachable:
|
||||
for i, l in enumerate(self.lookups):
|
||||
if hasattr(l, "wrapped_cache_key"):
|
||||
# setattr will force a __dict__ entry
|
||||
setattr(self, "_lookup%d_cache_key" % i,
|
||||
''.join(map(repr,
|
||||
l.wrapped_cache_key(self, style))))
|
||||
return CachedTemplate.cache_key(self, attr, style)
|
||||
|
||||
def __init__(self, *lookups, **context):
|
||||
self.lookups = lookups
|
||||
# set the default render class to be based on the lookup
|
||||
if self.__class__ == Wrapped and lookups:
|
||||
self.render_class = lookups[0].__class__
|
||||
else:
|
||||
self.render_class = self.__class__
|
||||
# this shouldn't be too surprising
|
||||
self.cache_ignore = self.cache_ignore.union(
|
||||
set(['cachable', 'render', 'cache_ignore', 'lookups']))
|
||||
if (not any(hasattr(l, "cachable") for l in lookups) and
|
||||
any(hasattr(l, "wrapped_cache_key") for l in lookups)):
|
||||
self.cachable = True
|
||||
if self.cachable:
|
||||
for l in lookups:
|
||||
if hasattr(l, "cache_ignore"):
|
||||
self.cache_ignore = self.cache_ignore.union(l.cache_ignore)
|
||||
|
||||
Templated.__init__(self, **context)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Wrapped: %s, %s>" % (self.__class__.__name__,
|
||||
self.lookups)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
#print "GETATTR: " + str(attr)
|
||||
#one would think this would never happen
|
||||
if attr == 'lookups':
|
||||
raise AttributeError, attr
|
||||
|
||||
@@ -61,59 +460,12 @@ class Wrapped(object):
|
||||
setattr(self, attr, res)
|
||||
return res
|
||||
|
||||
def __iter__(self):
|
||||
if self.lookups and hasattr(self.lookups[0], "__iter__"):
|
||||
return self.lookups[0].__iter__()
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s %s>' % (self.__class__.__name__, self.lookups)
|
||||
|
||||
def template(self, style = 'html'):
|
||||
from r2.config.templates import tpm
|
||||
from pylons import g
|
||||
debug = g.template_debug
|
||||
template = None
|
||||
if self.__class__ == Wrapped:
|
||||
for lookup in chain(self.lookups, (self.render_class,)):
|
||||
try:
|
||||
template = tpm.get(lookup, style, cache = not debug)
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
try:
|
||||
template = tpm.get(self, style, cache = not debug)
|
||||
except AttributeError:
|
||||
raise NoTemplateFound, (repr(self), style)
|
||||
|
||||
return template
|
||||
|
||||
#TODO is this the best way to override style?
|
||||
def render(self, style = None):
|
||||
"""Renders the template corresponding to this class in the given style."""
|
||||
from pylons import c
|
||||
style = style or c.render_style or 'html'
|
||||
template = self.template(style)
|
||||
if template:
|
||||
res = template.render(thing = self)
|
||||
return res if (style and style.startswith('api')) else unsafe(res)
|
||||
else:
|
||||
raise NoTemplateFound, repr(self)
|
||||
|
||||
def part_render(self, attr, *a, **kw):
|
||||
"""Renders the part of a template associated with the %def
|
||||
whose name is 'attr'. This is used primarily by
|
||||
r2.lib.menus.Styled"""
|
||||
style = kw.get('style', 'html')
|
||||
template = self.template(style)
|
||||
dt = template.get_def(attr)
|
||||
return unsafe(dt.render(thing = self, *a, **kw))
|
||||
|
||||
|
||||
def SimpleWrapped(**kw):
|
||||
class _SimpleWrapped(Wrapped):
|
||||
def __init__(self, *a, **kw1):
|
||||
kw.update(kw1)
|
||||
Wrapped.__init__(self, *a, **kw)
|
||||
return _SimpleWrapped
|
||||
|
||||
class Styled(Wrapped):
|
||||
class Styled(CachedTemplate):
|
||||
"""Rather than creating a separate template for every possible
|
||||
menu/button style we might want to use, this class overrides the
|
||||
render function to render only the <%def> in the template whose
|
||||
@@ -123,15 +475,16 @@ class Styled(Wrapped):
|
||||
are intended to be used in the outermost container's id and class
|
||||
tag.
|
||||
"""
|
||||
|
||||
def __init__(self, style, _id = '', css_class = '', **kw):
|
||||
self._id = _id
|
||||
self.css_class = css_class
|
||||
self.style = style
|
||||
Wrapped.__init__(self, **kw)
|
||||
CachedTemplate.__init__(self, **kw)
|
||||
|
||||
def render(self, **kw):
|
||||
"""Using the canonical template file, only renders the <%def>
|
||||
in the template whose name is given by self.style"""
|
||||
from pylons import c
|
||||
style = kw.get('style', c.render_style or 'html')
|
||||
return Wrapped.part_render(self, self.style, style = style, **kw)
|
||||
return CachedTemplate.part_render(self, self.style, **kw)
|
||||
|
||||
|
||||
|
||||
@@ -101,7 +101,9 @@ class Builder(object):
|
||||
for item in items:
|
||||
w = self.wrap(item)
|
||||
wrapped.append(w)
|
||||
|
||||
# add for caching (plus it should be bad form to use _
|
||||
# variables in templates)
|
||||
w.fullname = item._fullname
|
||||
types.setdefault(w.render_class, []).append(w)
|
||||
|
||||
#TODO pull the author stuff into add_props for links and
|
||||
@@ -124,16 +126,24 @@ class Builder(object):
|
||||
else:
|
||||
w.likes = None
|
||||
|
||||
#definite
|
||||
w.timesince = utils.timesince(item._date)
|
||||
|
||||
# update vote tallies
|
||||
compute_votes(w, item)
|
||||
|
||||
w.score = w.upvotes - w.downvotes
|
||||
if w.likes:
|
||||
base_score = w.score - 1
|
||||
elif w.likes is None:
|
||||
base_score = w.score
|
||||
else:
|
||||
base_score = w.score + 1
|
||||
# store the set of available scores based on the vote
|
||||
# for ease of i18n when there is a label
|
||||
w.voting_score = [max(base_score + x - 1, 0) for x in range(3)]
|
||||
|
||||
w.deleted = item._deleted
|
||||
|
||||
w.rowstyle = w.rowstyle if hasattr(w,'rowstyle') else ''
|
||||
w.rowstyle = getattr(w, 'rowstyle', "")
|
||||
w.rowstyle += ' ' + ('even' if (count % 2) else 'odd')
|
||||
|
||||
count += 1
|
||||
@@ -157,15 +167,16 @@ class Builder(object):
|
||||
w.can_ban = True
|
||||
if item._spam:
|
||||
w.show_spam = True
|
||||
if not hasattr(item,'moderator_banned'):
|
||||
w.moderator_banned = False
|
||||
|
||||
w.moderator_banned = getattr(item,'moderator_banned', False)
|
||||
w.autobanned, w.banner = ban_info.get(item._fullname,
|
||||
(False, None))
|
||||
|
||||
elif hasattr(item,'reported') and item.reported > 0:
|
||||
w.show_reports = True
|
||||
|
||||
# recache the user object: it may be None if user is not logged in,
|
||||
# whereas now we are happy to have the UnloggedUser object
|
||||
user = c.user
|
||||
for cls in types.keys():
|
||||
cls.add_props(user, types[cls])
|
||||
|
||||
|
||||
@@ -200,47 +200,16 @@ class Link(Thing, Printable):
|
||||
|
||||
return True
|
||||
|
||||
# none of these things will change over a link's lifetime
|
||||
cache_ignore = set(['subreddit', 'num_comments', 'link_child']
|
||||
).union(Printable.cache_ignore)
|
||||
@staticmethod
|
||||
def cache_key(wrapped):
|
||||
if c.user_is_admin:
|
||||
return False
|
||||
|
||||
link_child = wrapped.link_child
|
||||
s = (str(i) for i in (wrapped._fullname,
|
||||
bool(c.user_is_sponsor),
|
||||
bool(c.user_is_loggedin),
|
||||
wrapped.subreddit == c.site,
|
||||
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,
|
||||
wrapped.clicked,
|
||||
wrapped.hidden,
|
||||
wrapped.friend,
|
||||
wrapped.show_spam,
|
||||
wrapped.show_reports,
|
||||
wrapped.can_ban,
|
||||
wrapped.thumbnail,
|
||||
wrapped.moderator_banned,
|
||||
#link child stuff
|
||||
bool(link_child),
|
||||
bool(link_child) and link_child.load,
|
||||
bool(link_child) and link_child.expand
|
||||
))
|
||||
# htmllite depends on other get params
|
||||
s = ''.join(s)
|
||||
if c.render_style == "htmllite":
|
||||
s += ''.join(map(str, [request.get.has_key('style'),
|
||||
request.get.has_key('expanded'),
|
||||
request.get.has_key('twocolumn'),
|
||||
getattr(wrapped, 'embed_voting_style', None),
|
||||
c.bgcolor,
|
||||
c.bordercolor]))
|
||||
def wrapped_cache_key(wrapped, style):
|
||||
s = Printable.wrapped_cache_key(wrapped, style)
|
||||
if style == "htmllite":
|
||||
s.append(request.get.has_key('twocolumn'))
|
||||
elif style == "xml":
|
||||
s.append(request.GET.has_key("nothumbs"))
|
||||
return s
|
||||
|
||||
def make_permalink(self, sr, force_domain = False):
|
||||
@@ -266,23 +235,39 @@ class Link(Thing, Printable):
|
||||
from r2.lib.media import thumbnail_url
|
||||
from r2.lib.utils import timeago
|
||||
from r2.lib.template_helpers import get_domain
|
||||
from r2.models.subreddit import FakeSubreddit
|
||||
from r2.lib.wrapped import CachedVariable
|
||||
|
||||
saved = Link._saved(user, wrapped) if user else {}
|
||||
hidden = Link._hidden(user, wrapped) if user else {}
|
||||
# referencing c's getattr is cheap, but not as cheap when it
|
||||
# is in a loop that calls it 30 times on 25-200 things.
|
||||
user_is_admin = c.user_is_admin
|
||||
user_is_loggedin = c.user_is_loggedin
|
||||
pref_media = user.pref_media
|
||||
pref_frame = user.pref_frame
|
||||
pref_newwindow = user.pref_newwindow
|
||||
cname = c.cname
|
||||
site = c.site
|
||||
|
||||
saved = Link._saved(user, wrapped) if user_is_loggedin else {}
|
||||
hidden = Link._hidden(user, wrapped) if user_is_loggedin else {}
|
||||
#clicked = Link._clicked(user, wrapped) if user else {}
|
||||
clicked = {}
|
||||
|
||||
for item in wrapped:
|
||||
show_media = False
|
||||
if c.user.pref_compress:
|
||||
pass
|
||||
elif c.user.pref_media == 'on':
|
||||
if not hasattr(item, "score_fmt"):
|
||||
item.score_fmt = Score.number_only
|
||||
item.pref_compress = user.pref_compress
|
||||
if user.pref_compress:
|
||||
item.render_css_class = "compressed link"
|
||||
item.score_fmt = Score.points
|
||||
elif pref_media == 'on':
|
||||
show_media = True
|
||||
elif c.user.pref_media == 'subreddit' and item.subreddit.show_media:
|
||||
elif pref_media == 'subreddit' and item.subreddit.show_media:
|
||||
show_media = True
|
||||
elif (item.promoted
|
||||
and item.has_thumbnail
|
||||
and c.user.pref_media != 'off'):
|
||||
and pref_media != 'off'):
|
||||
show_media = True
|
||||
|
||||
if not show_media:
|
||||
@@ -292,7 +277,6 @@ class Link(Thing, Printable):
|
||||
else:
|
||||
item.thumbnail = g.default_thumb
|
||||
|
||||
|
||||
item.score = max(0, item.score)
|
||||
|
||||
item.domain = (domain(item.url) if not item.is_self
|
||||
@@ -304,23 +288,30 @@ class Link(Thing, Printable):
|
||||
item.hidden = bool(hidden.get((user, item, 'hide')))
|
||||
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 item.is_self:
|
||||
item.url = item.make_permalink(item.subreddit, force_domain = True)
|
||||
|
||||
if c.user_is_admin:
|
||||
# do we hide the score?
|
||||
if user_is_admin:
|
||||
item.hide_score = False
|
||||
elif item.promoted:
|
||||
item.hide_score = True
|
||||
elif c.user == item.author:
|
||||
elif user == item.author:
|
||||
item.hide_score = False
|
||||
elif item._date > timeago("2 hours"):
|
||||
item.hide_score = True
|
||||
else:
|
||||
item.hide_score = False
|
||||
|
||||
if c.user_is_loggedin and item.author._id == c.user._id:
|
||||
# store user preferences locally for caching
|
||||
item.pref_frame = pref_frame
|
||||
item.newwindow = pref_newwindow
|
||||
# is this link a member of a different (non-c.site) subreddit?
|
||||
item.different_sr = (isinstance(site, FakeSubreddit) or
|
||||
site.name != item.subreddit.name)
|
||||
|
||||
if user_is_loggedin and item.author._id == user._id:
|
||||
item.nofollow = False
|
||||
elif item.score <= 1 or item._spam or item.author._spam:
|
||||
item.nofollow = True
|
||||
@@ -328,11 +319,11 @@ class Link(Thing, Printable):
|
||||
item.nofollow = False
|
||||
|
||||
item.subreddit_path = item.subreddit.path
|
||||
if c.cname:
|
||||
if cname:
|
||||
item.subreddit_path = ("http://" +
|
||||
get_domain(cname = (c.site == item.subreddit),
|
||||
get_domain(cname = (site == item.subreddit),
|
||||
subreddit = False))
|
||||
if c.site != item.subreddit:
|
||||
if site != item.subreddit:
|
||||
item.subreddit_path += item.subreddit.path
|
||||
item.domain_path = "/domain/%s" % item.domain
|
||||
if item.is_self:
|
||||
@@ -353,7 +344,7 @@ class Link(Thing, Printable):
|
||||
item.editable = expand and item.author == c.user
|
||||
|
||||
item.tblink = "http://%s/tb/%s" % (
|
||||
get_domain(cname = c.cname, subreddit=False),
|
||||
get_domain(cname = cname, subreddit=False),
|
||||
item._id36)
|
||||
|
||||
if item.is_self:
|
||||
@@ -361,7 +352,7 @@ class Link(Thing, Printable):
|
||||
else:
|
||||
item.href_url = item.url
|
||||
|
||||
if c.user.pref_frame and not item.is_self:
|
||||
if pref_frame and not item.is_self:
|
||||
item.mousedown_url = item.tblink
|
||||
else:
|
||||
item.mousedown_url = None
|
||||
@@ -373,9 +364,20 @@ class Link(Thing, Printable):
|
||||
item._deleted,
|
||||
item._spam))
|
||||
|
||||
if c.user_is_loggedin:
|
||||
# bits that we will render stubs (to make the cached
|
||||
# version more flexible)
|
||||
item.num = CachedVariable("num")
|
||||
item.numcolmargin = CachedVariable("numcolmargin")
|
||||
item.commentcls = CachedVariable("commentcls")
|
||||
item.midcolmargin = CachedVariable("midcolmargin")
|
||||
item.comment_label = CachedVariable("numcomments")
|
||||
|
||||
if user_is_loggedin:
|
||||
incr_counts(wrapped)
|
||||
|
||||
# Run this last
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
@property
|
||||
def subreddit_slow(self):
|
||||
from subreddit import Subreddit
|
||||
@@ -395,9 +397,9 @@ class PromotedLink(Link):
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
Link.add_props(user, wrapped)
|
||||
|
||||
user_is_sponsor = c.user_is_sponsor
|
||||
try:
|
||||
if c.user_is_sponsor:
|
||||
if user_is_sponsor:
|
||||
promoted_by_ids = set(x.promoted_by
|
||||
for x in wrapped
|
||||
if hasattr(x,'promoted_by'))
|
||||
@@ -414,11 +416,14 @@ class PromotedLink(Link):
|
||||
for item in wrapped:
|
||||
# these are potentially paid for placement
|
||||
item.nofollow = True
|
||||
item.user_is_sponsor = user_is_sponsor
|
||||
if item.promoted_by in promoted_by_accounts:
|
||||
item.promoted_by_name = promoted_by_accounts[item.promoted_by].name
|
||||
else:
|
||||
# keep the template from trying to read it
|
||||
item.promoted_by = None
|
||||
# Run this last
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
class Comment(Thing, Printable):
|
||||
_data_int_props = Thing._data_int_props + ('reported',)
|
||||
@@ -478,33 +483,12 @@ class Comment(Thing, Printable):
|
||||
def keep_item(self, wrapped):
|
||||
return True
|
||||
|
||||
cache_ignore = set(["subreddit", "link", "to"]
|
||||
).union(Printable.cache_ignore)
|
||||
@staticmethod
|
||||
def cache_key(wrapped):
|
||||
if c.user_is_admin:
|
||||
return False
|
||||
|
||||
s = (str(i) for i in (c.profilepage,
|
||||
wrapped._fullname,
|
||||
bool(c.user_is_loggedin),
|
||||
c.focal_comment == wrapped._id36,
|
||||
request.host,
|
||||
c.cname,
|
||||
wrapped.author == c.user,
|
||||
wrapped.editted,
|
||||
wrapped.likes,
|
||||
wrapped.friend,
|
||||
wrapped.collapsed,
|
||||
wrapped.nofollow,
|
||||
wrapped.show_spam,
|
||||
wrapped.show_reports,
|
||||
wrapped.target,
|
||||
wrapped.can_ban,
|
||||
wrapped.moderator_banned,
|
||||
wrapped.can_reply,
|
||||
wrapped.deleted,
|
||||
wrapped.render_class,
|
||||
))
|
||||
s = ''.join(s)
|
||||
def wrapped_cache_key(wrapped, style):
|
||||
s = Printable.wrapped_cache_key(wrapped, style)
|
||||
s.extend([wrapped.body])
|
||||
return s
|
||||
|
||||
def make_permalink(self, link, sr=None):
|
||||
@@ -517,6 +501,7 @@ class Comment(Thing, Printable):
|
||||
@classmethod
|
||||
def add_props(cls, user, wrapped):
|
||||
#fetch parent links
|
||||
|
||||
links = Link._byID(set(l.link_id for l in wrapped), data = True,
|
||||
return_dict = True)
|
||||
|
||||
@@ -527,13 +512,21 @@ class Comment(Thing, Printable):
|
||||
|
||||
subreddits = Subreddit._byID(set(cm.sr_id for cm in wrapped),
|
||||
data=True,return_dict=False)
|
||||
can_reply_srs = set(s._id for s in subreddits if s.can_comment(user))
|
||||
can_reply_srs = set(s._id for s in subreddits if s.can_comment(user)) \
|
||||
if c.user_is_loggedin else set()
|
||||
|
||||
min_score = c.user.pref_min_comment_score
|
||||
min_score = user.pref_min_comment_score
|
||||
|
||||
cids = dict((w._id, w) for w in wrapped)
|
||||
|
||||
profilepage = c.profilepage
|
||||
user_is_admin = c.user_is_admin
|
||||
user_is_loggedin = c.user_is_loggedin
|
||||
focal_comment = c.focal_comment
|
||||
|
||||
for item in wrapped:
|
||||
# for caching:
|
||||
item.profilepage = c.profilepage
|
||||
item.link = links.get(item.link_id)
|
||||
|
||||
if not hasattr(item, 'subreddit'):
|
||||
@@ -554,32 +547,31 @@ class Comment(Thing, Printable):
|
||||
|
||||
# not deleted on profile pages,
|
||||
# deleted if spam and not author or admin
|
||||
item.deleted = (not c.profilepage and
|
||||
item.deleted = (not profilepage and
|
||||
(item._deleted or
|
||||
(item._spam and
|
||||
item.author != c.user and
|
||||
item.author != user and
|
||||
not item.show_spam)))
|
||||
|
||||
extra_css = ''
|
||||
if item._deleted:
|
||||
extra_css += "grayed"
|
||||
if not c.user_is_admin:
|
||||
if not user_is_admin:
|
||||
item.author = DeletedUser()
|
||||
item.body = '[deleted]'
|
||||
|
||||
|
||||
if c.focal_comment == item._id36:
|
||||
if focal_comment == item._id36:
|
||||
extra_css += 'border'
|
||||
|
||||
|
||||
# don't collapse for admins, on profile pages, or if deleted
|
||||
item.collapsed = ((item.score < min_score) and
|
||||
not (c.profilepage or
|
||||
not (profilepage or
|
||||
item.deleted or
|
||||
c.user_is_admin))
|
||||
user_is_admin))
|
||||
|
||||
if not hasattr(item,'editted'):
|
||||
item.editted = False
|
||||
item.editted = getattr(item, "editted", False)
|
||||
|
||||
#score less than 3, nofollow the links
|
||||
item.nofollow = item._score < 3
|
||||
@@ -589,32 +581,30 @@ class Comment(Thing, Printable):
|
||||
item.score_fmt = Score.points
|
||||
item.permalink = item.make_permalink(item.link, item.subreddit)
|
||||
|
||||
item.is_author = (user == item.author)
|
||||
item.is_focal = (focal_comment == item._id36)
|
||||
|
||||
#will seem less horrible when add_props is in pages.py
|
||||
from r2.lib.pages import UserText
|
||||
item.usertext = UserText(item, item.body,
|
||||
editable = item.author == c.user,
|
||||
editable = item.author == user,
|
||||
nofollow = item.nofollow,
|
||||
target = item.target,
|
||||
extra_css = extra_css)
|
||||
# Run this last
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
class StarkComment(Comment):
|
||||
"""Render class for the comments in the top-comments display in
|
||||
the reddit toolbar"""
|
||||
_nodb = True
|
||||
|
||||
class MoreComments(object):
|
||||
show_spam = False
|
||||
show_reports = False
|
||||
is_special = False
|
||||
can_ban = False
|
||||
deleted = False
|
||||
rowstyle = 'even'
|
||||
reported = False
|
||||
collapsed = False
|
||||
author = None
|
||||
margin = 0
|
||||
|
||||
class MoreComments(Printable):
|
||||
cachable = False
|
||||
display = ""
|
||||
|
||||
@staticmethod
|
||||
def cache_key(item):
|
||||
def wrapped_cache_key(item, style):
|
||||
return False
|
||||
|
||||
def __init__(self, link, depth, parent=None):
|
||||
@@ -647,7 +637,8 @@ class MoreChildren(MoreComments):
|
||||
class Message(Thing, Printable):
|
||||
_defaults = dict(reported = 0,)
|
||||
_data_int_props = Thing._data_int_props + ('reported', )
|
||||
|
||||
cache_ignore = set(["to"]).union(Printable.cache_ignore)
|
||||
|
||||
@classmethod
|
||||
def _new(cls, author, to, subject, body, ip, spam = False):
|
||||
m = Message(subject = subject,
|
||||
@@ -685,13 +676,15 @@ class Message(Thing, Printable):
|
||||
else:
|
||||
item.new = False
|
||||
item.score_fmt = Score.none
|
||||
# Run this last
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
|
||||
@staticmethod
|
||||
def cache_key(wrapped):
|
||||
#warning: inbox/sent messages
|
||||
#comments as messages
|
||||
return False
|
||||
def wrapped_cache_key(wrapped, style):
|
||||
s = Printable.wrapped_cache_key(wrapped, style)
|
||||
s.extend([c.msg_location])
|
||||
return s
|
||||
|
||||
|
||||
def keep_item(self, wrapped):
|
||||
return True
|
||||
|
||||
@@ -54,38 +54,11 @@ class Listing(object):
|
||||
|
||||
def get_items(self, *a, **kw):
|
||||
"""Wrapper around builder's get_items that caches the rendering."""
|
||||
from r2.lib.template_helpers import replace_render
|
||||
builder_items = self.builder.get_items(*a, **kw)
|
||||
|
||||
#render cache
|
||||
#fn to render non-boring items
|
||||
fullnames = {}
|
||||
for i in self.builder.item_iter(builder_items):
|
||||
rs = c.render_style
|
||||
key = i.render_class.cache_key(i)
|
||||
if key:
|
||||
fullnames[key + rs + c.lang] = i
|
||||
|
||||
def render_items(names):
|
||||
r = {}
|
||||
for i in names:
|
||||
item = fullnames[i]
|
||||
r[i] = item.render()
|
||||
return r
|
||||
|
||||
rendered_items = sgm(g.rendercache, fullnames, render_items, 'render_',
|
||||
time = g.page_cache_time)
|
||||
#replace the render function
|
||||
for k, v in rendered_items.iteritems():
|
||||
def make_fn(v):
|
||||
default = c.render_style
|
||||
default_render = fullnames[k].render
|
||||
def r(style = default):
|
||||
if style != c.render_style:
|
||||
return default_render(style = style)
|
||||
return v
|
||||
return r
|
||||
fullnames[k].render = make_fn(v)
|
||||
|
||||
for item in self.builder.item_iter(builder_items):
|
||||
# rewrite the render method
|
||||
item.render = replace_render(self, item, item.render)
|
||||
return builder_items
|
||||
|
||||
def listing(self):
|
||||
@@ -104,6 +77,9 @@ class Listing(object):
|
||||
#TODO: need name for template -- must be better way
|
||||
return Wrapped(self)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.things)
|
||||
|
||||
class LinkListing(Listing):
|
||||
def __init__(self, *a, **kw):
|
||||
Listing.__init__(self, *a, **kw)
|
||||
|
||||
@@ -19,10 +19,52 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from pylons import c, request
|
||||
from r2.lib.strings import Score
|
||||
class Printable(object):
|
||||
show_spam = False
|
||||
show_reports = False
|
||||
is_special = False
|
||||
can_ban = False
|
||||
deleted = False
|
||||
rowstyle = 'even'
|
||||
reported = False
|
||||
collapsed = False
|
||||
author = None
|
||||
margin = 0
|
||||
is_focal = False
|
||||
childlisting = None
|
||||
cache_ignore = set(['author', 'score_fmt', 'child',
|
||||
# displayed score is cachable, so remove score
|
||||
# related fields.
|
||||
'voting_score', 'display_score',
|
||||
'render_score', 'score', '_score',
|
||||
'upvotes', '_ups',
|
||||
'downvotes', '_downs',
|
||||
'subreddit_slow',
|
||||
'cachable', 'make_permalink', 'permalink',
|
||||
'timesince', 'votehash'
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def add_props(cls, listing, wrapped):
|
||||
pass
|
||||
def add_props(cls, user, wrapped):
|
||||
from r2.lib.wrapped import CachedVariable
|
||||
for item in wrapped:
|
||||
# insert replacement variable for timesince to allow for
|
||||
# caching of thing templates
|
||||
item.display = CachedVariable("display")
|
||||
item.timesince = CachedVariable("timesince")
|
||||
item.votehash = CachedVariable("votehash")
|
||||
item.childlisting = CachedVariable("childlisting")
|
||||
|
||||
score_fmt = getattr(item, "score_fmt", Score.number_only)
|
||||
item.display_score = map(score_fmt, item.voting_score)
|
||||
|
||||
if item.cachable:
|
||||
item.render_score = item.display_score
|
||||
item.display_score = map(CachedVariable,
|
||||
["scoredislikes", "scoreunvoted",
|
||||
"scorelikes"])
|
||||
|
||||
@property
|
||||
def permalink(self, *a, **kw):
|
||||
@@ -30,3 +72,14 @@ class Printable(object):
|
||||
|
||||
def keep_item(self, wrapped):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def wrapped_cache_key(wrapped, style):
|
||||
s = [wrapped._fullname, wrapped._spam]
|
||||
|
||||
if style == 'htmllite':
|
||||
s.extend([c.bgcolor, c.bordercolor,
|
||||
request.get.has_key('style'),
|
||||
request.get.get("expanded"),
|
||||
getattr(wrapped, 'embed_voting_style', None)])
|
||||
return s
|
||||
|
||||
@@ -248,33 +248,21 @@ class Subreddit(Thing, Printable):
|
||||
if not user or not user.has_subscribed:
|
||||
item.subscriber = item._id in defaults
|
||||
else:
|
||||
item.subscriber = rels.get((item, user, 'subscriber'))
|
||||
item.moderator = rels.get((item, user, 'moderator'))
|
||||
item.contributor = item.moderator or \
|
||||
rels.get((item, user, 'contributor'))
|
||||
item.subscriber = bool(rels.get((item, user, 'subscriber')))
|
||||
item.moderator = bool(rels.get((item, user, 'moderator')))
|
||||
item.contributor = bool(item.moderator or \
|
||||
rels.get((item, user, 'contributor')))
|
||||
item.score = item._ups
|
||||
item.score_fmt = Score.subscribers
|
||||
|
||||
Printable.add_props(user, wrapped)
|
||||
#TODO: make this work
|
||||
cache_ignore = set(["subscribers"]).union(Printable.cache_ignore)
|
||||
@staticmethod
|
||||
def cache_key(wrapped):
|
||||
if c.user_is_admin:
|
||||
return False
|
||||
|
||||
s = (str(i) for i in (wrapped._fullname,
|
||||
bool(c.user_is_loggedin),
|
||||
wrapped.subscriber,
|
||||
wrapped.moderator,
|
||||
wrapped.contributor,
|
||||
wrapped._spam))
|
||||
s = ''.join(s)
|
||||
def wrapped_cache_key(wrapped, style):
|
||||
s = Printable.wrapped_cache_key(wrapped, style)
|
||||
s.extend([wrapped._spam])
|
||||
return s
|
||||
|
||||
#TODO: make this work
|
||||
#@property
|
||||
#def author_id(self):
|
||||
#return 1
|
||||
|
||||
@classmethod
|
||||
def top_lang_srs(cls, lang, limit):
|
||||
"""Returns the default list of subreddits for a given language, sorted
|
||||
|
||||
@@ -740,7 +740,6 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.likes div.score.likes {display: block;}
|
||||
.dislikes div.score.dislikes {display: block;}
|
||||
|
||||
|
||||
.warm-entry .rank { color: #EDA179; }
|
||||
.hot-entry .rank { color: #E47234; }
|
||||
.cool-entry .rank { color: #A5ABFB; }
|
||||
|
||||
@@ -281,6 +281,7 @@ function share(elem) {
|
||||
$(elem).new_thing_child($(".sharelink:first").clone(true)
|
||||
.attr("id", "sharelink_" + $(elem).thing_id()),
|
||||
false);
|
||||
$.request("new_captcha");
|
||||
};
|
||||
|
||||
function cancelShare(elem) {
|
||||
@@ -888,7 +889,6 @@ function reply(elem) {
|
||||
form.find(".cancel").get(0).onclick = function() {form.hide()};
|
||||
}
|
||||
|
||||
|
||||
function populate_click_gadget() {
|
||||
/* if we can find the click-gadget, populate it */
|
||||
if($('.click-gadget').length) {
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
body {
|
||||
font-family: verdana,arial,helvetica,sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: x-small;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #369;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.link {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: blue;
|
||||
font-size: small;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
.byline {
|
||||
margin: 0px 0px .5em 2px;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.domain {
|
||||
color: #369;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.child {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.headerbar {
|
||||
background:lightgray none repeat scroll 0%;
|
||||
margin: 5px 0px 5px 2px;
|
||||
}
|
||||
|
||||
.headerbar span {
|
||||
background-color: white;
|
||||
color: gray;
|
||||
font-size: x-small;
|
||||
font-weight: bold;
|
||||
margin-left: 15px;
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
.score {
|
||||
margin: 0px .5em 0px .5em;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.nextprev {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.tabmenu {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.tabmenu li {
|
||||
display: inline;
|
||||
margin-right: .25em;
|
||||
padding-left: .25em;
|
||||
border-left: thin solid #000;
|
||||
}
|
||||
|
||||
.redditname {
|
||||
font-weight: bold;
|
||||
margin: 0px 3px 0px 3px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pagename {
|
||||
font-weight: bold;
|
||||
margin-right: 1ex;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.or {
|
||||
border-left:thin solid #000000;
|
||||
border-right:thin solid #000000;
|
||||
padding: 0px .25em 0px .25em;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #CEE3F8;
|
||||
}
|
||||
|
||||
#header .redditname a{
|
||||
color: black;
|
||||
}
|
||||
|
||||
#header img {
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
/* markdown */
|
||||
.md { max-width: 60em; overflow: auto; }
|
||||
.md p, .md h1 { margin-bottom: .5em;}
|
||||
.md h1 { font-weight: normal; font-size: 100%; }
|
||||
.md > * { margin-bottom: 0px }
|
||||
.md strong { font-weight: bold; }
|
||||
.md em { font-style: italic; }
|
||||
.md img { display: none }
|
||||
.md ol, .md ul { margin: 10px 2em; }
|
||||
.md pre { margin: 10px; }
|
||||
@@ -25,5 +25,5 @@
|
||||
%>
|
||||
|
||||
%if c.user_is_admin:
|
||||
${AppServiceMonitor().render()}
|
||||
${AppServiceMonitor()}
|
||||
%endif
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="translation.html" import="statusbar"/>
|
||||
<%namespace file="printable.html" import="ynbutton, state_button" />
|
||||
<%namespace file="printablebuttons.html" import="ynbutton, state_button" />
|
||||
<%
|
||||
from r2.lib.translation import Translator
|
||||
%>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<%def name="bodyHTML()">
|
||||
<body style="background-color: ${'#%s' % (c.bgcolor or 'FFFFFF')}"
|
||||
class="button-body">
|
||||
<div class="button ${thing_css_class(thing.link) if thing.link else ''}">
|
||||
<div class="button ${thing_css_class(thing) if thing._fullname else ''}">
|
||||
%if not c.user_is_loggedin:
|
||||
<div id="cover" style="display: none">
|
||||
<div class="cover"></div>
|
||||
|
||||
@@ -27,11 +27,16 @@
|
||||
<%namespace file="buttontypes.html" import="submiturl" />
|
||||
|
||||
<%
|
||||
domain = get_domain()
|
||||
domain = get_domain()
|
||||
if thing._fullname:
|
||||
path = thing.make_permalink_slow()
|
||||
else:
|
||||
path = capture(submiturl, thing.url, thing.title)
|
||||
%>
|
||||
(function() {
|
||||
var styled_submit = '<a style="color: #369; text-decoration: none;" href="${submiturl(thing.url)}" target="${thing.target}">';
|
||||
var unstyled_submit = '<a href="${submiturl(thing.url)}" target="${thing.target}">';
|
||||
|
||||
var styled_submit = '<a style="color: #369; text-decoration: none;" href="${path}" target="${thing.target}">';
|
||||
var unstyled_submit = '<a href="${submiturl(thing.url)}" target="${path}">';
|
||||
var write_string='<span class="reddit_button" style="';
|
||||
%if thing.styled:
|
||||
write_string += 'color: grey;';
|
||||
@@ -40,8 +45,8 @@
|
||||
%if thing.image > 0:
|
||||
write_string += unstyled_submit + '<img style="height: 2.3ex; vertical-align:top; margin-right: 1ex" src="http://${get_domain(subreddit=False)}/static/spreddit${thing.image}.gif">' + "</a>";
|
||||
%endif
|
||||
%if thing.link:
|
||||
write_string += '${Score.safepoints(thing.link.score)}';
|
||||
%if thing._fullname:
|
||||
write_string += '${Score.safepoints(thing.score)}';
|
||||
%if thing.styled:
|
||||
write_string += ' on ' + styled_submit + 'reddit</a>';
|
||||
%else:
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<%!
|
||||
from r2.lib.template_helpers import get_domain, static, style_line, choose_width
|
||||
from r2.lib.utils import query_string
|
||||
from r2.lib.strings import Score
|
||||
%>
|
||||
|
||||
<%namespace file="printable.html" import="arrow, score" />
|
||||
@@ -36,9 +37,9 @@
|
||||
<%def name="class_def(class_number, width=None)">
|
||||
<%
|
||||
like_cls = "unvoted"
|
||||
if getattr(thing.link, "likes", None):
|
||||
if getattr(thing, "likes", None):
|
||||
like_cls = "likes"
|
||||
elif getattr(thing.link, "likes", None) is False:
|
||||
elif getattr(thing, "likes", None) is False:
|
||||
like_cls = "dislikes"
|
||||
%>
|
||||
<div class="blog blog${class_number} entry ${like_cls}"
|
||||
@@ -46,41 +47,46 @@
|
||||
style="${style_line(width)}"
|
||||
%endif
|
||||
>
|
||||
${caller.body()}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="button1(thing)">
|
||||
${class_def(1, width=choose_width(thing.link, thing.width))}
|
||||
<%self:class_def class_number="1"
|
||||
width="${choose_width(thing, thing.width)}">
|
||||
<div class="headimgcell">
|
||||
<a href="${submiturl(thing.url, thing.title)}" target="${thing.target}">
|
||||
<img src="http://www.reddit.com/static/blog_head.png" alt=""/>
|
||||
</a>
|
||||
</div>
|
||||
%if thing.link:
|
||||
%if thing._fullname:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
${score(thing.link, thing.likes, tag='div')}
|
||||
${arrow(thing, 1, thing.likes)}
|
||||
${arrow(thing, 0, thing.likes == False)}
|
||||
${score(thing, thing.likes,
|
||||
scores = map(Score.safepoints, thing.voting_score),
|
||||
tag='div')}
|
||||
%else:
|
||||
${thing.link.score}
|
||||
${thing.score}
|
||||
%endif
|
||||
%else:
|
||||
${submitlink(thing.url, thing.title)}
|
||||
%endif
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</%self:class_def>
|
||||
</%def>
|
||||
|
||||
<%def name="button2(thing)">
|
||||
${class_def(2)}
|
||||
%if thing.link:
|
||||
<%self:class_def class_number="2">
|
||||
%if thing._fullname:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, tag='div')}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
${arrow(thing, 1, thing.likes)}
|
||||
${score(thing, thing.likes, tag='div')}
|
||||
${arrow(thing, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
${thing.link.score}
|
||||
<br />
|
||||
<br />
|
||||
${thing.score}
|
||||
<br />
|
||||
%endif
|
||||
%else:
|
||||
<img src="http://www.reddit.com/static/blog_head.png" alt=""/>
|
||||
@@ -93,21 +99,21 @@ ${class_def(1, width=choose_width(thing.link, thing.width))}
|
||||
%endif
|
||||
<a href="${submiturl(thing.url, thing.title)}" target="${thing.target}">reddit</a>
|
||||
</div>
|
||||
</div>
|
||||
</%self:class_def>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="button3(thing)">
|
||||
${class_def(3)}
|
||||
<%self:class_def class_number="3">
|
||||
<div class="left">
|
||||
%if thing.link:
|
||||
%if thing._fullname:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, tag='div')}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
${arrow(thing, 1, thing.likes)}
|
||||
${score(thing, thing.likes, tag='div')}
|
||||
${arrow(thing, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
${thing.link.score}
|
||||
${thing.score}
|
||||
<br />
|
||||
%endif
|
||||
%else:
|
||||
@@ -120,47 +126,48 @@ ${class_def(3)}
|
||||
${img_link('submit', '/static/blog_snoo.gif', capture(submiturl, thing.url, thing.title), target=thing.target)}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</%self:class_def>
|
||||
</%def>
|
||||
|
||||
<%def name="button4(thing)">
|
||||
${class_def(2)}
|
||||
%if thing.link:
|
||||
<%self:class_def class_number="2">
|
||||
%if thing._fullname:
|
||||
%if thing.vote:
|
||||
${arrow(thing.link, 1, thing.likes)}
|
||||
${score(thing.link, thing.likes, tag='div')}
|
||||
${arrow(thing.link, 0, thing.likes == False)}
|
||||
${arrow(thing, 1, thing.likes)}
|
||||
${score(thing, thing.likes, tag='div')}
|
||||
${arrow(thing, 0, thing.likes == False)}
|
||||
%else:
|
||||
<br />
|
||||
${thing.link.score}
|
||||
${thing.score}
|
||||
<br />
|
||||
%endif
|
||||
%else:
|
||||
<img src="http://www.reddit.com/static/blog_head.png" alt=""/>
|
||||
${submitlink(thing.url, thing.title, 'submit to')}
|
||||
%endif
|
||||
</div>
|
||||
</%self:class_def>
|
||||
</%def>
|
||||
|
||||
<%def name="button5(thing)">
|
||||
${class_def(5, width=choose_width(thing.link, thing.width))}
|
||||
<%self:class_def class_number="5"
|
||||
width="${choose_width(thing, thing.width)}">
|
||||
${img_link('submit', '/static/blog_snoo.gif', capture(submiturl, thing.url, thing.title), _class="left")}
|
||||
<%
|
||||
# this button style submits to the cname as a default
|
||||
c.cname = hasattr(c.site, "domain") and bool(c.site.domain)
|
||||
submitlink = capture(submiturl, thing.url, thing.title)
|
||||
if thing.link:
|
||||
fullname = thing.link._fullname
|
||||
ups = thing.link.upvotes
|
||||
downs = thing.link.downvotes
|
||||
if thing._fullname:
|
||||
fullname = thing._fullname
|
||||
ups = thing.upvotes
|
||||
downs = thing.downvotes
|
||||
link = "http://%s%s" % (get_domain(cname = c.cname, subreddit = False),
|
||||
thing.link.make_permalink_slow())
|
||||
thing.make_permalink_slow())
|
||||
else:
|
||||
fullname = ""
|
||||
ups = 0
|
||||
downs = 0
|
||||
link = submitlink
|
||||
if c.user_is_loggedin:
|
||||
if c.user_is_loggedin and thing._fullname:
|
||||
dir = 1 if thing.likes else 0 if thing.likes is None else -1
|
||||
ups = ups - (1 if dir > 0 else 0)
|
||||
downs = downs - (1 if dir < 0 else 0)
|
||||
@@ -206,7 +213,7 @@ ${class_def(3)}
|
||||
</li>
|
||||
<li>
|
||||
<a href="${link}" target="_new">
|
||||
%if thing.link:
|
||||
%if thing._fullname:
|
||||
${_("Discuss at the %(name)s reddit") % dict(name = c.site.name)}
|
||||
%else:
|
||||
${_("Submit to the %(name)s reddit") % dict(name = c.site.name)}
|
||||
@@ -214,7 +221,7 @@ ${class_def(3)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="votes ${'' if thing.link else 'disabled'}">
|
||||
<div class="votes ${'' if thing._fullname else 'disabled'}">
|
||||
%for i in range(3):
|
||||
<div class="right" id="controversy${i}_${fullname}"
|
||||
${"" if i == dir + 1 else "style='display:none'"}>
|
||||
@@ -223,7 +230,7 @@ ${class_def(3)}
|
||||
%endfor
|
||||
<a class="arrow up${'mod' if dir==1 else ''}"
|
||||
id="up_${fullname}"
|
||||
%if c.user_is_loggedin and thing.link:
|
||||
%if c.user_is_loggedin and thing._fullname:
|
||||
href="#" onclick="$(this).vote('', set_score); return false; "
|
||||
%else:
|
||||
href="${submitlink}" target="_new"
|
||||
@@ -236,7 +243,7 @@ ${class_def(3)}
|
||||
%endfor
|
||||
</a>
|
||||
<a class="arrow down${'mod' if dir==-1 else ''}" id="down_${fullname}"
|
||||
%if c.user_is_loggedin and thing.link:
|
||||
%if c.user_is_loggedin and thing._fullname:
|
||||
href="#" onclick="$(this).vote('', set_score); return false;"
|
||||
%else:
|
||||
href="${submitlink}" target="_new"
|
||||
@@ -251,5 +258,5 @@ ${class_def(3)}
|
||||
</div>
|
||||
<div class="clearleft"><!--IEsux--></div>
|
||||
</div>
|
||||
</div>
|
||||
</%self:class_def>
|
||||
</%def>
|
||||
|
||||
@@ -19,14 +19,14 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%namespace file="printable.html" import="simple_button" />
|
||||
<%namespace file="printablebuttons.html" import="simple_button" />
|
||||
|
||||
%if c.user.pref_clickgadget:
|
||||
<div class="gadget" style="${'display: none' if not thing.content else ''}">
|
||||
<h2>${_("Recently viewed links")}</h2>
|
||||
|
||||
<div class="click-gadget">
|
||||
${thing.content}
|
||||
${unsafe(thing.content)}
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
<%!
|
||||
from r2.lib.filters import edit_comment_filter, unsafe, safemarkdown
|
||||
from r2.lib.utils import to36
|
||||
from r2.lib.pages.things import CommentButtons
|
||||
%>
|
||||
<%inherit file="comment_skeleton.html"/>
|
||||
|
||||
@@ -76,7 +76,8 @@ ${parent.collapsed()}
|
||||
%if show:
|
||||
${unsafe(self.score(thing, likes = thing.likes))} 
|
||||
%endif
|
||||
${_("%(timeago)s ago") % dict(timeago=thing.timesince)}
|
||||
## thing.timesince is a cache stub
|
||||
${unsafe(_("%(timeago)s ago") % dict(timeago=thing.timesince))}
|
||||
%if thing.editted:
|
||||
<em>*</em>
|
||||
%endif
|
||||
@@ -106,41 +107,11 @@ ${parent.Child(not thing.collapsed)}
|
||||
</%def>
|
||||
|
||||
<%def name="arrows()">
|
||||
${parent.midcol()}
|
||||
${parent.midcol()}
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="buttons()">
|
||||
<li class="first">
|
||||
${parent.bylink_button(_("permalink"), thing.permalink)}
|
||||
</li>
|
||||
|
||||
%if thing.deleted:
|
||||
%if thing.parent_permalink and not c.profilepage:
|
||||
<li>
|
||||
${parent.bylink_button(_("parent"), thing.parent_permalink)}
|
||||
</li>
|
||||
%endif
|
||||
%else:
|
||||
%if not c.profilepage:
|
||||
%if thing.parent_permalink:
|
||||
<li>
|
||||
${parent.bylink_button(_("parent"), thing.parent_permalink)}
|
||||
</li>
|
||||
%endif
|
||||
%if thing.usertext.editable:
|
||||
<li>
|
||||
${parent.simple_button(_("edit"), "edit_usertext")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
${parent.delete_or_report_buttons()}
|
||||
${parent.buttons()}
|
||||
%if not c.profilepage and thing.can_reply:
|
||||
<li>
|
||||
${parent.simple_button(_("reply"), "reply")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
${CommentButtons(thing)}
|
||||
${self.admintagline()}
|
||||
</%def>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<%inherit file="printable.htmllite" />
|
||||
|
||||
<%def name="parent()">
|
||||
%if isBlog:
|
||||
%if c.profilepage:
|
||||
<small>
|
||||
<a href="${thing.ancestor.url}">${thing.ancestor.titleprefix}${thing.ancestor.title}</a></small><br \>
|
||||
%endif
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<%inherit file="printable.mobile" />
|
||||
|
||||
<%def name="parent()">
|
||||
%if isBlog:
|
||||
%if c.profilepage:
|
||||
<small>
|
||||
<a href="${thing.ancestor.url}">${thing.ancestor.titleprefix}${thing.ancestor.title}</a></small><br \>
|
||||
%endif
|
||||
|
||||
@@ -41,4 +41,4 @@
|
||||
<description>${body|h}</description>
|
||||
</item>
|
||||
|
||||
${hasattr(thing, "child") and thing.child.render() or ''}
|
||||
${hasattr(thing, "child") and thing.child or ''}
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%def name="midcol(display=True, cls = '')">
|
||||
%if c.profilepage or (not thing._deleted and (not thing._spam or c.user == thing.author or c.user_is_admin)):
|
||||
${parent.midcol(display=display, cls = cls)}
|
||||
%endif
|
||||
%if c.profilepage or (not thing._deleted and (not thing._spam or thing.is_author or c.user_is_admin)):
|
||||
${parent.midcol(display=display, cls = cls)}
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="tagline(collapse=False)">
|
||||
@@ -37,7 +37,7 @@ ${self.tagline(True)}
|
||||
</%def>
|
||||
|
||||
<%def name="commentBody()">
|
||||
${thing.usertext.render(style="html")}
|
||||
${thing.usertext}
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -56,7 +56,3 @@ ${self.tagline(True)}
|
||||
</ul>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="bylink_button(title, link)">
|
||||
${plain_link(title, link, _class="bylink", rel="nofollow")}
|
||||
</%def>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="printable.html" import="toggleable_label"/>
|
||||
<%namespace file="printablebuttons.html" import="toggleable_label"/>
|
||||
|
||||
<div class="comments-panel">
|
||||
|
||||
@@ -42,6 +42,6 @@
|
||||
reverse = not c.user.pref_frame_commentspanel)}
|
||||
</div>
|
||||
|
||||
${thing.listing.render()}
|
||||
${thing.listing}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -58,15 +58,13 @@
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('message')}">
|
||||
${UserText(None, have_form = False, creating = True).render()}
|
||||
<%utils:round_field title="message">
|
||||
${UserText(None, have_form = False, creating = True)}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
${thing.captcha}
|
||||
</div>
|
||||
|
||||
<button class="btn" type="submit">${_("send")}</button>
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
|
||||
<%inherit file="reddit.html"/>
|
||||
<%namespace file="utils.html" import="plain_link, logout"/>
|
||||
<%namespace file="printable.html" import="state_button, comment_button, thing_css_class, score" />
|
||||
<%namespace file="printablebuttons.html" import="state_button, comment_button"/>
|
||||
<%namespace file="printable.html" import="thing_css_class, score" />
|
||||
|
||||
<%def name="javascript_run()">
|
||||
${parent.javascript_run()}
|
||||
@@ -56,7 +57,7 @@
|
||||
%endif
|
||||
</a>
|
||||
|
||||
%if thing.link:
|
||||
%if thing._fullname:
|
||||
${withlink()}
|
||||
%endif
|
||||
</div>
|
||||
@@ -89,7 +90,7 @@
|
||||
<a href="http://${get_domain(cname = c.cname)}/user/${c.user.name}"
|
||||
title="${_('visit your userpage')}"
|
||||
class="clickable" target="_top">${c.user.name}</a>
|
||||
%elif thing.link:
|
||||
%elif thing._fullname:
|
||||
<a href="${thing.loginurl}" target="_top" class="clickable">
|
||||
${_("login / register")}
|
||||
</a>
|
||||
@@ -118,14 +119,14 @@
|
||||
</div>
|
||||
|
||||
<div class="middle-side clickable">
|
||||
%if thing.link:
|
||||
%if thing._fullname:
|
||||
<a
|
||||
title="${_('click to visit the main page for this submission')}"
|
||||
class="title clickable"
|
||||
target="_top"
|
||||
href="${thing.link.make_permalink_slow()}"
|
||||
href="${thing.permalink}"
|
||||
>
|
||||
${thing.link.title}
|
||||
${thing.title}
|
||||
|
||||
%if thing.domain:
|
||||
<span class="domain"> (${thing.domain})</span>
|
||||
@@ -156,15 +157,15 @@
|
||||
|
||||
|
||||
<%def name="withlink()">
|
||||
<span class="${thing_css_class(thing.link)}">
|
||||
<span class="${thing_css_class(thing)}">
|
||||
## add us to the click cookie
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
add_thing_id_to_cookie("${thing.link._fullname}", "recentclicks2");
|
||||
add_thing_id_to_cookie("${thing._fullname}", "recentclicks2");
|
||||
});
|
||||
</script>
|
||||
<span>
|
||||
${score(thing.link, thing.link.likes, score_fmt = Score.safepoints, tag='b')}
|
||||
<span class="entry ${'likes' if thing.likes else 'dislikes' if thing.likes is False else 'unvoted'}">
|
||||
${score(thing, thing.likes, tag='b')}
|
||||
</span>
|
||||
<span class="arrows">
|
||||
<%def name="arrow(direction, style, message)">
|
||||
@@ -172,7 +173,7 @@
|
||||
title="${_('vote %(direction)s') % dict(direction=direction)}"
|
||||
%if c.user_is_loggedin:
|
||||
href="javascript:void(0);"
|
||||
onclick="$(this).vote('${thing.vh}')"
|
||||
onclick="$(this).vote('${thing.votehash}')"
|
||||
%else:
|
||||
href="${thing.loginurl}"
|
||||
target="_top"
|
||||
@@ -189,7 +190,7 @@
|
||||
|
||||
|
||||
%if c.user_is_loggedin:
|
||||
%if thing.link.saved:
|
||||
%if thing.saved:
|
||||
${state_button("unsave", _("unsave"),
|
||||
"return change_state(this, 'unsave');", "<b>%s</b>" % _("unsaved"),
|
||||
a_class="clickable")}
|
||||
@@ -200,13 +201,13 @@
|
||||
%endif
|
||||
%endif
|
||||
|
||||
<a href="/toolbar/comments/${thing.link._id36}" target="reddit_panel"
|
||||
<a href="/toolbar/comments/${thing._id36}" target="reddit_panel"
|
||||
title="${_('toggle the comments panel')}"
|
||||
onclick="toolbar.comments_pushed(this); return toolbar.panel_loadurl(this.href);"
|
||||
class="comments comments-button">${thing.com_label}</a>
|
||||
class="comments comments-button">${thing.comment_label}</a>
|
||||
</span>
|
||||
|
||||
%if thing.link and thing.expanded:
|
||||
%if thing._fullname and thing.expanded:
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
toolbar.comments_pushed($('.comments-button'));
|
||||
|
||||
@@ -21,21 +21,21 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.models.subreddit import Default
|
||||
from r2.lib.template_helpers import get_domain
|
||||
%>
|
||||
|
||||
from r2.lib.pages.things import LinkButtons
|
||||
%>
|
||||
|
||||
<%inherit file="printable.html"/>
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
<%namespace file="printablebuttons.html" import="toggle_button" />
|
||||
|
||||
<%def name="numcol()">
|
||||
<% num = thing.num %>
|
||||
<span class="rank" style="width:$numcolmargin;">
|
||||
<span class="rank" style="width:${thing.numcolmargin};">
|
||||
%if thing.top_link:
|
||||
<a class="star" href="/toplinks">
|
||||
%endif
|
||||
##placeholder for the number
|
||||
$num
|
||||
${thing.num}
|
||||
%if thing.top_link:
|
||||
</a>
|
||||
%endif
|
||||
@@ -48,7 +48,7 @@
|
||||
%if thing.nofollow:
|
||||
rel="nofollow"
|
||||
%endif
|
||||
%if c.user.pref_newwindow:
|
||||
%if thing.newwindow:
|
||||
target="_blank"
|
||||
%elif c.cname:
|
||||
target="_top"
|
||||
@@ -131,14 +131,14 @@
|
||||
</%def>
|
||||
|
||||
<%def name="midcol(display=True, cls = '')">
|
||||
%if c.user.pref_compress:
|
||||
<div class="midcol ${cls}" style="width:2ex;"
|
||||
%if thing.pref_compress:
|
||||
<div class="midcol ${cls}" style="width:2ex;"
|
||||
%else:
|
||||
<div class="midcol ${cls}" style="width:$midcolmargin;"
|
||||
<div class="midcol ${cls}" style="width:${thing.midcolmargin};"
|
||||
%endif
|
||||
${not display and "style='display:none'" or ''}>
|
||||
${self.arrow(thing, 1, thing.likes)}
|
||||
%if c.user.pref_compress:
|
||||
%if thing.pref_compress:
|
||||
<div class="score-placeholder"></div>
|
||||
%elif thing.hide_score:
|
||||
<div class="score likes">•</div>
|
||||
@@ -161,19 +161,15 @@
|
||||
|
||||
<%def name="tagline()">
|
||||
<%
|
||||
from r2.lib.utils import timeago
|
||||
from r2.models import FakeSubreddit
|
||||
from r2.lib.strings import Score
|
||||
|
||||
if isinstance(c.site, FakeSubreddit) or c.site != thing.subreddit:
|
||||
if thing.different_sr:
|
||||
taglinetext = _("submitted %(when)s ago by %(author)s to %(reddit)s")
|
||||
else:
|
||||
taglinetext = _("submitted %(when)s ago by %(author)s")
|
||||
taglinetext = taglinetext.replace(" ", " ")
|
||||
%>
|
||||
|
||||
%if c.user.pref_compress and not thing.hide_score:
|
||||
${self.score(thing, thing.likes, score_fmt = Score.safepoints, tag='span')}
|
||||
%if thing.pref_compress and not thing.hide_score:
|
||||
${self.score(thing, thing.likes, tag='span')}
|
||||
 
|
||||
%endif
|
||||
|
||||
@@ -185,54 +181,9 @@
|
||||
<%def name="child()">
|
||||
</%def>
|
||||
|
||||
<%def name="buttons(comments=True,delete=True,report=True,ban=True,additional='')">
|
||||
<% fullname = thing._fullname %>
|
||||
%if comments:
|
||||
<%
|
||||
if not thing.num_comments:
|
||||
# generates "comment" the imperative verb
|
||||
com_label = _("comment {verb}")
|
||||
else:
|
||||
# generates "XX comments" as a noun
|
||||
com_label = ungettext("comment", "comments", thing.num_comments)
|
||||
%>
|
||||
<li class="first">
|
||||
${parent.comment_button("comment", com_label,
|
||||
thing.num_comments, thing.permalink,
|
||||
newwindow = c.user.pref_newwindow)}
|
||||
</li>
|
||||
%endif
|
||||
%if thing.editable:
|
||||
<li>
|
||||
${parent.simple_button(_("edit"), "edit_usertext")}
|
||||
</li>
|
||||
%endif
|
||||
<li class="share">
|
||||
${parent.toggle_button("share-button", _("share"), _("cancel"),
|
||||
"share", "cancelShare", login_required = True)}
|
||||
</li>
|
||||
%if c.user_is_loggedin:
|
||||
<li>
|
||||
%if thing.saved:
|
||||
${parent.state_button("unsave", _("unsave"), \
|
||||
"return change_state(this, 'unsave', unsave_thing);", _("unsaved"))}
|
||||
%else:
|
||||
${parent.state_button("save", _("save"), \
|
||||
"return change_state(this, 'save', save_thing);", _("saved"))}
|
||||
%endif
|
||||
</li><li>
|
||||
%if thing.hidden:
|
||||
${parent.state_button("unhide", _("unhide"), \
|
||||
"change_state(this, 'unhide', hide_thing);", _("unhidden"))}
|
||||
%else:
|
||||
${parent.state_button("hide", _("hide"), \
|
||||
"change_state(this, 'hide', hide_thing);", _("hidden"))}
|
||||
%endif
|
||||
</li>
|
||||
%endif
|
||||
${parent.delete_or_report_buttons(delete=delete,report=report)}
|
||||
${parent.buttons(ban=ban)}
|
||||
${additional}
|
||||
<%def name="buttons(comments=True, delete=True, report=True, ban=True, additional='')">
|
||||
${LinkButtons(thing, comments = comments, delete = delete,
|
||||
report = report, ban = ban)}
|
||||
</%def>
|
||||
|
||||
<%def name="thumbnail()">
|
||||
|
||||
@@ -31,21 +31,10 @@
|
||||
<%def name="entry()">
|
||||
<%
|
||||
from r2.lib.strings import Score
|
||||
if thing.num_comments:
|
||||
# generates "XX comments" as a noun
|
||||
com_label = "%d %s" % \
|
||||
(thing.num_comments,
|
||||
ungettext("comment", "comments", thing.num_comments))
|
||||
else:
|
||||
# generates "comment" the imperative verb
|
||||
com_label = _("comment")
|
||||
|
||||
domain = get_domain(subreddit=False)
|
||||
permalink = "http://%s%s" % (domain, thing.permalink)
|
||||
expanded = request.get.get("expanded")
|
||||
two_col = request.get.has_key("twocolumn") if l else False
|
||||
|
||||
thing.score_fmt = Score.safepoints
|
||||
%>
|
||||
${self.arrows(thing)}
|
||||
<div class="reddit-entry entry ${thing.like_cls}"
|
||||
@@ -84,7 +73,7 @@
|
||||
%endif
|
||||
<a class="reddit-comment-link"
|
||||
${optionalstyle("color:gray")}
|
||||
href="${permalink}">${com_label}</a>
|
||||
href="${permalink}">${thing.comment_label}</a>
|
||||
</small>
|
||||
</div>
|
||||
<div class="reddit-link-end" ${optionalstyle("clear:left; padding:3px;")}>
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
from r2.models import FakeSubreddit
|
||||
%>
|
||||
<%
|
||||
com_label = ungettext("comment", "comments", thing.num_comments)
|
||||
url = add_sr(thing.permalink, force_hostname = True)
|
||||
use_thumbs = thing.thumbnail and not request.GET.has_key("nothumbs")
|
||||
%>
|
||||
@@ -37,24 +36,31 @@
|
||||
<pubDate>${thing._date.strftime('%a, %d %b %Y %H:%M:%S %z')}</pubDate>
|
||||
<dc:date>${thing._date.isoformat()}</dc:date>
|
||||
<description>
|
||||
%if use_thumbs:
|
||||
<table>
|
||||
<tr><td>
|
||||
<a href="${url}"><img src="${thing.thumbnail}" alt="${thing.title}" title="${thing.title}" /></a>
|
||||
</td><td>
|
||||
%endif
|
||||
<% domain = get_domain(cname = c.cname, subreddit = False) %>
|
||||
submitted by <a href="http://${domain}/user/${thing.author.name}">${thing.author.name}</a>
|
||||
%if isinstance(c.site, FakeSubreddit):
|
||||
to <a href="http://${domain}${thing.subreddit.path}">
|
||||
${thing.subreddit.name}</a>
|
||||
%endif
|
||||
<br/>
|
||||
<a href="${thing.url}">[link]</a>
|
||||
<a href="${url}">[${thing.num_comments} ${com_label}]</a>
|
||||
%if use_thumbs:
|
||||
</td></tr></table>
|
||||
%endif
|
||||
<%def name="description()" filter="h">
|
||||
%if use_thumbs:
|
||||
<table>
|
||||
<tr><td>
|
||||
<a href="${url}"><img src="${thing.thumbnail}" alt="${thing.title}" title="${thing.title}" /></a>
|
||||
</td><td>
|
||||
%endif
|
||||
<%
|
||||
domain = get_domain(cname = c.cname, subreddit = False)
|
||||
%>
|
||||
submitted by <a href="http://${domain}/user/${thing.author.name}">
|
||||
${thing.author.name}
|
||||
</a>
|
||||
%if thing.different_sr:
|
||||
to <a href="http://${domain}${thing.subreddit.path}">
|
||||
${thing.subreddit.name}</a>
|
||||
%endif
|
||||
<br/>
|
||||
<a href="${thing.url}">[link]</a>
|
||||
<a href="${url}">[${thing.comment_label}]</a>
|
||||
%if use_thumbs:
|
||||
</td></tr></table>
|
||||
%endif
|
||||
</%def>
|
||||
${description()}
|
||||
</description>
|
||||
%if use_thumbs:
|
||||
<media:title>${thing.title}</media:title>
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%namespace file="printable.html" import="state_button, thing_css_class" />
|
||||
<%namespace file="printablebuttons.html" import="state_button" />
|
||||
<%namespace file="printable.html" import="thing_css_class" />
|
||||
|
||||
<div class="raisedbox linkinfo">
|
||||
<table class="details">
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<%!
|
||||
from datetime import datetime
|
||||
%>
|
||||
<%namespace file="printable.html" import="ynbutton" />
|
||||
<%namespace file="printablebuttons.html" import="ynbutton" />
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
%if thing.a.promoted:
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<% from r2.lib.template_helpers import replace_render, add_sr %>
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%
|
||||
@@ -29,7 +28,7 @@
|
||||
%>
|
||||
<div id="siteTable${_id}" class="sitetable ${cls}">
|
||||
%for a in thing.things:
|
||||
${unsafe(replace_render(thing, a))}
|
||||
${a}
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%! from r2.lib.template_helpers import replace_render %>
|
||||
|
||||
<%namespace file="utils.html" import="optionalstyle"/>
|
||||
<%namespace file="printable.html" import="thing_css_class"/>
|
||||
<div ${optionalstyle("margin-left:5px;margin-top:7px;")}>
|
||||
@@ -48,11 +46,7 @@
|
||||
%endif
|
||||
|
||||
<div class="${cls} ${thing_css_class(a)}">
|
||||
%if getattr(a, 'embed_voting_style', None) == 'votable':
|
||||
${unsafe(replace_render(thing,a))}
|
||||
%else:
|
||||
${a.render()}
|
||||
%endif
|
||||
${a}
|
||||
</div>
|
||||
%if two_col and i == l - 1:
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
<ul>
|
||||
%for a in thing.things:
|
||||
${a.render()}
|
||||
${a}
|
||||
%endfor:
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
%for a in thing.things:
|
||||
${a.render()}
|
||||
${a}
|
||||
%endfor:
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
%if not isContent:
|
||||
<span id="listing_error${parent!=UNDEFINED and '_%s'%str(parent) or ''}" class="error">${emptyStr}</span>
|
||||
%endif
|
||||
${ listing.render() }
|
||||
%if prev or next:
|
||||
<p class="menu"> ${"view more"}:
|
||||
%if prev:
|
||||
<a href="${prev}"
|
||||
>« ${"prev"}</a>
|
||||
%endif
|
||||
%if next and prev:
|
||||
|
|
||||
%endif
|
||||
%if next:
|
||||
<a href="${next}"
|
||||
>${"next"} »</a>
|
||||
%endif
|
||||
</p>
|
||||
%endif
|
||||
@@ -30,13 +30,13 @@
|
||||
<% op = "login-main" %>
|
||||
<form method="post"
|
||||
id="login_${op}" action="${add_sr('/post/login', nocname = True)}"
|
||||
%if not c.frameless_cname or c.authorized_cname:
|
||||
%if thing.auth_cname:
|
||||
onsubmit="return post_user(this, 'login');"
|
||||
%else:
|
||||
onsubmit="return update_user(this);"
|
||||
%endif
|
||||
class="login-form-side">
|
||||
%if c.cname:
|
||||
%if thing.cname:
|
||||
<input type="hidden" name="${UrlParser.cname_get}"
|
||||
value="${random.random()}" />
|
||||
%endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="menuarea">
|
||||
%for menu in thing.menus:
|
||||
<div class="spacer">
|
||||
${menu.render()}
|
||||
${menu}
|
||||
</div>
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
<%!
|
||||
from r2.lib.filters import edit_comment_filter, safemarkdown
|
||||
from r2.lib.pages.things import MessageButtons
|
||||
%>
|
||||
<%inherit file="comment_skeleton.html"/>
|
||||
|
||||
@@ -67,20 +68,7 @@ ${unsafe(safemarkdown(thing.body))}
|
||||
</%def>
|
||||
|
||||
<%def name="buttons()">
|
||||
%if hasattr(thing, "was_comment"):
|
||||
<li>
|
||||
${parent.bylink_button(_("context"), thing.permalink + "?context=3")}
|
||||
</li>
|
||||
${parent.delete_or_report_buttons(delete=False)}
|
||||
%else:
|
||||
${parent.delete_or_report_buttons(delete=False)}
|
||||
${parent.buttons()}
|
||||
%if c.user_is_loggedin:
|
||||
<li>
|
||||
${parent.simple_button(_("reply"), "reply")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
${MessageButtons(thing)}
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
|
||||
@@ -51,14 +51,12 @@
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('message')}">
|
||||
${UserText(None, have_form = False, creating = True).render()}
|
||||
${UserText(None, have_form = False, creating = True)}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
${thing.captcha}
|
||||
</div>
|
||||
|
||||
<button id="send" name="send" type="submit">${_("send")}</button>
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%def name="plain(selected = False)">
|
||||
${plain_link(thing.selected_title() if selected else thing.title,
|
||||
<%def name="plain()">
|
||||
${plain_link(thing.selected_title() if thing.selected else thing.title,
|
||||
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,
|
||||
<%def name="js()">
|
||||
${plain_link(thing.selected_title() if thing.selected else thing.title,
|
||||
thing.path, _sr_path = False, nocname = True,
|
||||
_class = thing.css_class, _id = thing._id, onclick = thing.onclick)}
|
||||
_class = thing.css_class, _id = thing._id,
|
||||
onclick = thing.onclick)}
|
||||
</%def>
|
||||
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
|
||||
<%inherit file="navbutton.html" />
|
||||
|
||||
<%def name="plain(selected = False)">
|
||||
${parent.plain(selected=selected)}
|
||||
<%def name="plain()">
|
||||
${parent.plain()}
|
||||
</%def>
|
||||
|
||||
<%def name="js(selected = False)">
|
||||
<%def name="js()">
|
||||
</%def>
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
<%namespace file="utils.html" import="plain_link, text_with_links, img_link, separator"/>
|
||||
|
||||
<%def name="dropdown()">
|
||||
## caching comment:
|
||||
## see caching comment for plain_link. In addition to the args,
|
||||
## this function depends on c.site.name, c.render_style and c.cname.
|
||||
<% css_class = str(thing.css_class) if thing.css_class else "" %>
|
||||
%if thing:
|
||||
%if thing.title and thing.selected:
|
||||
@@ -69,16 +72,15 @@
|
||||
##option.title isn't the title, option.render() is the entire link
|
||||
if option == thing.selected:
|
||||
class_name = "class='selected'"
|
||||
content = option.render(selected=True)
|
||||
option.selected = True
|
||||
else:
|
||||
class_name = ""
|
||||
content = option.render()
|
||||
%>
|
||||
<li ${class_name}>
|
||||
%if i > 0:
|
||||
${separator(thing.separator)}
|
||||
%endif
|
||||
${content}
|
||||
${option}
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
@@ -102,7 +104,7 @@
|
||||
%if option == thing.selected:
|
||||
<li class="selected ${cls}">${option.selected_title()}</li>
|
||||
%else:
|
||||
<li class="${cls}">${option.render()}</li>
|
||||
<li class="${cls}">${option}</li>
|
||||
%endif
|
||||
%endfor
|
||||
</ul>
|
||||
@@ -116,10 +118,10 @@
|
||||
${"id='%s'" % thing._id if thing._id else ""}>
|
||||
%for i, option in enumerate(thing):
|
||||
<%
|
||||
selected = (option == thing.selected)
|
||||
option.selected = (option == thing.selected)
|
||||
%>
|
||||
<li ${"class='selected'" if selected else ""}>
|
||||
${option.render(selected = selected)}
|
||||
<li ${"class='selected'" if option.selected else ""}>
|
||||
${option}
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
_class="submit content",
|
||||
_id="newlink">
|
||||
|
||||
${thing.formtabs_menu.render()}
|
||||
${thing.formtabs_menu}
|
||||
<div class="formtabs-content">
|
||||
|
||||
<div class="spacer">
|
||||
@@ -69,7 +69,7 @@ ${thing.formtabs_menu.render()}
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('text')}", description="${_('(optional)')}" id="text-field">
|
||||
<input name="kind" value="self" type="hidden"/>
|
||||
${UserText(None, have_form = False, creating = True).render()}
|
||||
${UserText(None, have_form = False, creating = True)}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
@@ -108,9 +108,7 @@ ${thing.formtabs_menu.render()}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
${thing.captcha}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -126,5 +124,5 @@ ${thing.formtabs_menu.render()}
|
||||
if(form.length) {
|
||||
var default_menu = form.find(".${thing.default_tab}:first");
|
||||
select_form_tab(default_menu, "${thing.default_show}", "${thing.default_hide}");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="printable.html" import="ynbutton"/>
|
||||
<%namespace file="printablebuttons.html" import="ynbutton"/>
|
||||
<%
|
||||
from r2.lib.template_helpers import replace_render, static
|
||||
from r2.lib.template_helpers import static
|
||||
from r2.lib.promote import get_promoted
|
||||
%>
|
||||
|
||||
@@ -43,8 +43,7 @@
|
||||
<% pass %>
|
||||
%elif lookup.has_key(name):
|
||||
<% seen.add(name) %>
|
||||
${unsafe(replace_render(thing, lookup[name],
|
||||
display = (thing.visible_link == name)))}
|
||||
${unsafe(lookup[name].render(display = (thing.visible_link == name)))}
|
||||
%else:
|
||||
<div class="thing id-${name} stub" style="display:none"></div>
|
||||
%endif
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
${thing.content and thing.content.render() or ''}
|
||||
@@ -1,23 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
${thing.content and thing.content.render() or ''}
|
||||
@@ -30,7 +30,7 @@
|
||||
<h1>${thing.title}</h1>
|
||||
%endif
|
||||
|
||||
${t.render() if t else ''}
|
||||
${t}
|
||||
|
||||
%if thing.div:
|
||||
</div>
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
################################################################################
|
||||
|
||||
%for t in thing.stack:
|
||||
${t.render()}
|
||||
${t}
|
||||
%endfor
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
################################################################################
|
||||
|
||||
%for t in thing.stack:
|
||||
${t.render() if t else ""}
|
||||
${t}
|
||||
%endfor
|
||||
|
||||
@@ -21,5 +21,5 @@
|
||||
################################################################################
|
||||
|
||||
%for t in thing.stack:
|
||||
${t.render() if t else ''}
|
||||
${t}
|
||||
%endfor
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
<%!
|
||||
from r2.lib.template_helpers import add_sr
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.pages.things import BanButtons
|
||||
%>
|
||||
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
@@ -61,9 +62,6 @@ thing id-${what._fullname}
|
||||
else:
|
||||
cls = thing.lookups[0].__class__.__name__.lower()
|
||||
|
||||
if cls == 'link' and c.user.pref_compress:
|
||||
cls = 'compressed link'
|
||||
|
||||
if thing.show_spam:
|
||||
rowclass = thing.rowstyle + " spam"
|
||||
elif thing.show_reports:
|
||||
@@ -75,7 +73,7 @@ thing id-${what._fullname}
|
||||
if hasattr(thing, "hidden") and thing.hidden:
|
||||
rowclass += " hidden"
|
||||
%>
|
||||
<div class="${self.thing_css_class(thing)} ${rowclass} ${cls}" $display>
|
||||
<div class="${self.thing_css_class(thing)} ${rowclass} ${cls}" ${thing.display}>
|
||||
<p class="parent">
|
||||
${self.ParentDiv()}
|
||||
</p>
|
||||
@@ -98,43 +96,9 @@ thing id-${what._fullname}
|
||||
</%def>
|
||||
|
||||
<%def name="buttons(ban=True)">
|
||||
%if thing.can_ban and ban:
|
||||
%if thing.show_spam:
|
||||
<li>
|
||||
${self.state_button("unban", _("unban"),
|
||||
"return change_state(this, 'unban');", _("unbanned"))}
|
||||
</li>
|
||||
%else:
|
||||
<li>
|
||||
${self.state_button("ban", _("ban"),
|
||||
"return change_state(this, 'ban');", _("banned"))}
|
||||
</li>
|
||||
%endif
|
||||
%if thing.show_reports:
|
||||
<li>
|
||||
${self.state_button("ignore", _("ignore"), \
|
||||
"change_state(this, 'ignore');", _("ignored"))}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
${BanButtons(thing, show_ban = ban)}
|
||||
</%def>
|
||||
|
||||
<%def name="delete_or_report_buttons(delete=True, report=True)">
|
||||
%if c.user_is_loggedin:
|
||||
%if (not thing.author or thing.author.name != c.user.name) and report:
|
||||
<li>
|
||||
${ynbutton(_("report"), _("reported"), "report", "hide_thing")}
|
||||
</li>
|
||||
%endif
|
||||
%if (not thing.author or thing.author.name == c.user.name) and delete and not thing._deleted:
|
||||
<li>
|
||||
${ynbutton(_("delete"), _("deleted"), "del", "hide_thing")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="ParentDiv()">
|
||||
</%def>
|
||||
|
||||
@@ -144,11 +108,9 @@ thing id-${what._fullname}
|
||||
<%def name="entry()">
|
||||
</%def>
|
||||
|
||||
<%def name="Child(display=True, render_hook=True)">
|
||||
<%def name="Child(display=True)">
|
||||
<div class="child" ${(not display and "style='display:none'" or "")}>
|
||||
%if render_hook:
|
||||
$child
|
||||
%endif
|
||||
${thing.childlisting}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
@@ -193,7 +155,7 @@ thing id-${what._fullname}
|
||||
%>
|
||||
<div class="arrow ${_class}"
|
||||
%if c.user_is_loggedin:
|
||||
onclick="$(this).vote('$votehash')"
|
||||
onclick="$(this).vote('${thing.votehash}')"
|
||||
%else:
|
||||
onclick="showcover(true, 'vote_${fullname}')"
|
||||
%endif
|
||||
@@ -201,23 +163,16 @@ thing id-${what._fullname}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<%def name="score(this, likes=None, tag='span', score_fmt = None)">
|
||||
<%
|
||||
score = this.score
|
||||
base_score = score - 1 if likes else score if likes is None else score + 1
|
||||
base_score = [base_score + x for x in range(-1, 2)]
|
||||
if score_fmt is None:
|
||||
score_fmt = thing.score_fmt
|
||||
%>
|
||||
<${tag} class="score dislikes">
|
||||
${score_fmt(base_score[0])}
|
||||
</${tag}>
|
||||
<${tag} class="score unvoted">
|
||||
${score_fmt(base_score[1])}
|
||||
</${tag}>
|
||||
<${tag} class="score likes">
|
||||
${score_fmt(base_score[2])}
|
||||
</${tag}>
|
||||
<%def name="score(this, likes=None, scores = None, tag='span')">
|
||||
<%
|
||||
if scores is None:
|
||||
scores = this.display_score
|
||||
%>
|
||||
%for cls, score in zip(["dislikes", "unvoted", "likes"], scores):
|
||||
<${tag} class="score ${cls}">
|
||||
${score}
|
||||
</${tag}>
|
||||
%endfor
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -228,151 +183,3 @@ thing id-${what._fullname}
|
||||
${self.arrow(thing, 0, thing.likes == False)}
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
##
|
||||
##
|
||||
##
|
||||
### originally in statebuttons
|
||||
<%def name="state_button(name, title, onclick, executed, clicked=False, a_class = '', fmt=None, fmt_param = '', hidden_data = {})">
|
||||
<%def name="_link()" buffered="True">
|
||||
<a href="javascript:void()"
|
||||
%if a_class:
|
||||
class="${a_class}"
|
||||
%endif
|
||||
onclick="${onclick}">${title}</a>
|
||||
</%def>
|
||||
<%
|
||||
link = _link()
|
||||
if fmt:
|
||||
link = fmt % {fmt_param: link}
|
||||
## preserve spaces before and after < & > for space compression
|
||||
link = link.replace(" <", " <").replace("> ", "> ")
|
||||
%>
|
||||
|
||||
%if clicked:
|
||||
${executed}
|
||||
%else:
|
||||
<form action="/post/${name}" method="post"
|
||||
class="state-button ${name}-button">
|
||||
<input type="hidden" name="executed" value="${executed}" />
|
||||
%for key, value in hidden_data.iteritems():
|
||||
<input type="hidden" name="${key}" value="${value}" />
|
||||
%endfor
|
||||
<span>
|
||||
${unsafe(link)}
|
||||
</span>
|
||||
</form>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="ynbutton(title, executed, op, callback = 'null',
|
||||
question = None,
|
||||
format = '%(link)s',
|
||||
format_arg = 'link',
|
||||
hidden_data = {})">
|
||||
<%
|
||||
if question is None:
|
||||
question = _("are you sure?")
|
||||
link = ('<a href="#" onclick="return toggle(this)">'
|
||||
+ title + '</a>')
|
||||
link = format % {format_arg : link}
|
||||
link = unsafe(link.replace(" <", " <").replace("> ", "> "))
|
||||
%>
|
||||
<form class="toggle ${op}-button" action="#" method="get">
|
||||
<input type="hidden" name="executed" value="${executed}"/>
|
||||
%for k, v in hidden_data.iteritems():
|
||||
<input type="hidden" name="${k}" value="${v}"/>
|
||||
%endfor
|
||||
<span class="option active">
|
||||
${link}
|
||||
</span>
|
||||
<span class="option error">
|
||||
${question}
|
||||
 <a href="javascript:void(0)" class="yes"
|
||||
onclick='change_state(this, "${op}", ${callback})'>
|
||||
${_("yes")}
|
||||
</a> / 
|
||||
<a href="javascript:void(0)" class="no"
|
||||
onclick="return toggle(this)">${_("no")}</a>
|
||||
</span>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
<%def name="simple_button(title, nameFunc)">
|
||||
<a class="" href="javascript:void(0)"
|
||||
onclick="return ${nameFunc}(this)">${title}</a>
|
||||
</%def>
|
||||
|
||||
<%def name="toggle_button(class_name, title, alt_title,
|
||||
callback, cancelback,
|
||||
css_class = '', alt_css_class = '',
|
||||
reverse = False,
|
||||
login_required = False,
|
||||
style = '',)">
|
||||
<%
|
||||
if reverse:
|
||||
callback, cancelback = cancelback, callback
|
||||
title, alt_title = alt_title, title
|
||||
css_class, alt_css_class = alt_css_class, css_class
|
||||
%>
|
||||
<span class="${class_name} toggle" style="${style}">
|
||||
<a class="option active ${css_class}" href="#" tabindex="100"
|
||||
%if login_required and not c.user_is_loggedin:
|
||||
onclick="return showcover('', '${callback}_' + $(this).thing_id());"
|
||||
%else:
|
||||
onclick="return toggle(this, ${callback}, ${cancelback})"
|
||||
%endif
|
||||
>
|
||||
${title}
|
||||
</a>
|
||||
<a class="option ${alt_css_class}" href="#">${alt_title}</a>
|
||||
</span>
|
||||
</%def>
|
||||
|
||||
<%def name="toggleable_label(class_name,
|
||||
title, alt_title,
|
||||
callback, cancelback,
|
||||
reverse = False)">
|
||||
<%
|
||||
if reverse:
|
||||
callback, cancelback = cancelback, callback
|
||||
title, alt_title = alt_title, title
|
||||
%>
|
||||
<span class="${class_name} toggle">
|
||||
<span class="toggle option active">${title}</span>
|
||||
<span class="toggle option">${alt_title}</span>
|
||||
 (
|
||||
<a href="#"
|
||||
onclick="return toggle_label(this, ${callback}, ${cancelback})"
|
||||
>
|
||||
${_("toggle")}
|
||||
</a>
|
||||
)
|
||||
</span>
|
||||
</%def>
|
||||
|
||||
<%def name="tags(**kw)">
|
||||
%for k, v in kw.iteritems():
|
||||
%if v is not None:
|
||||
${k.strip('_')}="${v}" \
|
||||
%endif
|
||||
%endfor
|
||||
</%def>
|
||||
|
||||
|
||||
### originally in commentbutton
|
||||
<%def name="comment_button(name, link_text, num, link,\
|
||||
a_class='', title='', newwindow = False)">
|
||||
<%
|
||||
cls = "comments empty" if num == 0 else "comments"
|
||||
|
||||
if num > 0:
|
||||
link_text = strings.number_label % dict(num=num, thing=link_text)
|
||||
|
||||
target = '_blank' if newwindow else '_parent'
|
||||
%>
|
||||
${plain_link(link_text, link,
|
||||
_class="%s %s" % (a_class, cls), title=title, target=target)}
|
||||
|
||||
</%def>
|
||||
|
||||
@@ -39,7 +39,7 @@ ${self.Child()}
|
||||
</%def>
|
||||
|
||||
<%def name="Child()">
|
||||
${hasattr(thing, "child") and thing.child.render() or ''}
|
||||
${getattr(thing, "child", '')}
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<%def name="Child()">
|
||||
%if hasattr(thing, "child"):
|
||||
<div id="child_${thing._fullname}" class="child">
|
||||
${hasattr(thing, "child") and thing.child.render() or ''}
|
||||
${thing.child}
|
||||
</div>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
from r2.lib.utils import timesince
|
||||
%>
|
||||
<%namespace file="utils.html" import="submit_form, plain_link"/>
|
||||
<%namespace file="printable.html" import="toggle_button"/>
|
||||
<%namespace file="printablebuttons.html" import="toggle_button"/>
|
||||
<% user = thing.user %>
|
||||
%if thing.user:
|
||||
<div class="raisedbox">
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
%>
|
||||
|
||||
<%inherit file="link.html"/>
|
||||
<%namespace file="printablebuttons.html" import="ynbutton" />
|
||||
<%namespace file="utils.html" import="plain_link" />
|
||||
|
||||
<%def name="tagline()">
|
||||
@@ -36,7 +37,7 @@
|
||||
_sr_path = False)}
|
||||
</li>
|
||||
<li>
|
||||
${self.ynbutton(_("unpromote"), _("unpromoted"), "unpromote")}
|
||||
${ynbutton(_("unpromote"), _("unpromoted"), "unpromote")}
|
||||
</li>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
from r2.lib.utils import to36
|
||||
%>
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
<%namespace file="printable.html" import="ynbutton"/>
|
||||
<%namespace file="printablebuttons.html" import="ynbutton"/>
|
||||
|
||||
|
||||
<div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
from r2.lib.media import thumbnail_url
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field, checkbox, plain_link, image_upload" />
|
||||
<%namespace file="printable.html" import="ynbutton"/>
|
||||
<%namespace file="printablebuttons.html" import="ynbutton"/>
|
||||
<%namespace file="utils.html" import="error_field, checkbox, image_upload" />
|
||||
|
||||
%if thing.link:
|
||||
|
||||
@@ -21,10 +21,13 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import add_sr, static, join_urls, class_dict, path_info
|
||||
from r2.lib.template_helpers import add_sr, static, join_urls, class_dict, path_info, get_domain
|
||||
from r2.lib.pages import SearchForm, ClickGadget
|
||||
from r2.lib import tracking
|
||||
from pylons import request
|
||||
from r2.lib.strings import strings
|
||||
%>
|
||||
<%namespace file="login.html" import="login_panel, login_form"/>
|
||||
<%namespace file="framebuster.html" import="framebuster"/>
|
||||
<%inherit file="base.html"/>
|
||||
|
||||
@@ -37,8 +40,6 @@
|
||||
</%def>
|
||||
|
||||
<%def name="stylesheet()">
|
||||
<% from r2.lib.template_helpers import static, get_domain %>
|
||||
|
||||
%if c.lang_rtl:
|
||||
<link rel="stylesheet" href="${static(g.stylesheet_rtl)}"
|
||||
type="text/css" />
|
||||
@@ -112,21 +113,52 @@
|
||||
##<div class="fixedwidth"><!--IE6sux--></div>
|
||||
##<div class="clearleft"><!--IE6sux--></div>
|
||||
<div class="content">
|
||||
${thing.content().render()}
|
||||
${thing.content()}
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if thing.footer:
|
||||
<%include file="redditfooter.html"/>
|
||||
%endif
|
||||
|
||||
${thing.footer}
|
||||
|
||||
%if not c.user_is_loggedin:
|
||||
%if thing.enable_login_cover:
|
||||
<div class="login-popup cover-overlay" style="display: none">
|
||||
<div class="cover" onclick="return hidecover(this)"></div>
|
||||
<div class="popup">
|
||||
<h1 class="cover-msg">${strings.cover_msg}</h1>
|
||||
${login_panel(login_form)}
|
||||
<div style="text-align:center; clear: both">
|
||||
<a href="#" onclick="return hidecover(this)">
|
||||
${_("close this window")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
<div class="lang-popup cover-overlay" style="display: none">
|
||||
<div class="cover" onclick="return hidecover(this)"></div>
|
||||
<div class="popup">
|
||||
<%include file="prefoptions.html" />
|
||||
<div style="text-align:center; clear:both;">
|
||||
<a href="#" onclick="return hidecover(this)">
|
||||
${_("close this window")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if g.tracker_url and thing.site_tracking:
|
||||
<img alt="" src="${tracking.UserInfo.gen_url()}"/>
|
||||
%endif
|
||||
%endif
|
||||
${framebuster()}
|
||||
|
||||
</body>
|
||||
</%def>
|
||||
|
||||
<%def name="sidebar(content=None)">
|
||||
${content.render() if content else ''}
|
||||
${content}
|
||||
|
||||
%if c.user_is_admin:
|
||||
<%include file="admin_rightbox.html"/>
|
||||
@@ -135,7 +167,7 @@
|
||||
%endif
|
||||
|
||||
##cheating... we should move ads into a template of its own
|
||||
${ClickGadget(c.recent_clicks).render()}
|
||||
${ClickGadget(c.recent_clicks)}
|
||||
</%def>
|
||||
|
||||
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
|
||||
<%inherit file="base.htmllite"/>
|
||||
|
||||
${thing.content and thing.content().render() or ''}
|
||||
${thing.content and thing.content() or ''}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
<%include file="redditheader.mobile"/>
|
||||
|
||||
${thing.content and thing.content().render() or ''}
|
||||
${thing.content and thing.content() or ''}
|
||||
|
||||
<%def name="Title()">
|
||||
%if thing.title:
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
|
||||
<%inherit file="base.xml"/>
|
||||
|
||||
${thing.content().render()}
|
||||
${thing.content()}
|
||||
|
||||
@@ -21,65 +21,29 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.lib.template_helpers import get_domain, static, UrlParser
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib import tracking
|
||||
import random, datetime
|
||||
import datetime
|
||||
%>
|
||||
<%namespace file="login.html" import="login_panel, login_form"/>
|
||||
<%namespace file="utils.html" import="text_with_links, plain_link"/>
|
||||
<%namespace file="utils.html" import="text_with_links"/>
|
||||
|
||||
<div class="footer-parent">
|
||||
<div class="footer rounded">
|
||||
%for toolbar in thing.footer_nav():
|
||||
%for toolbar in thing.nav():
|
||||
<div class="col">
|
||||
${toolbar.render()}
|
||||
${toolbar}
|
||||
</div>
|
||||
%endfor
|
||||
|
||||
<p class="bottommenu">
|
||||
${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"))}.
|
||||
${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"))}.
|
||||
${_("(c) %(year)d Conde Nast Digital. All rights reserved.") % \
|
||||
dict(year=datetime.datetime.now().timetuple()[0])}
|
||||
</p>
|
||||
%if g.tracker_url and thing.site_tracking:
|
||||
<img alt="" src="${tracking.UserInfo.gen_url()}"/>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
%if not c.user_is_loggedin:
|
||||
%if thing.enable_login_cover:
|
||||
${self.loginpopup(login_panel, login_form)}
|
||||
%endif
|
||||
${self.langpopup()}
|
||||
%endif
|
||||
|
||||
<%def name="loginpopup(lp, lf)">
|
||||
<div class="login-popup cover-overlay" style="display: none">
|
||||
<div class="cover" onclick="return hidecover(this)"></div>
|
||||
<div class="popup">
|
||||
<h1 class="cover-msg">${strings.cover_msg}</h1>
|
||||
${lp(lf)}
|
||||
<div style="text-align:center; clear: both">
|
||||
<a href="#" onclick="return hidecover(this)">
|
||||
${_("close this window")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="langpopup()">
|
||||
<div class="lang-popup cover-overlay" style="display: none">
|
||||
<div class="cover" onclick="return hidecover(this)"></div>
|
||||
<div class="popup">
|
||||
<%include file="prefoptions.html" />
|
||||
<div style="text-align:center; clear:both;">
|
||||
<a href="#" onclick="return hidecover(this)">
|
||||
${_("close this window")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
@@ -29,10 +29,7 @@
|
||||
<%namespace file="utils.html" import="plain_link, text_with_js, img_link, separator, logout"/>
|
||||
|
||||
<div id="header">
|
||||
%if thing.srtopbar:
|
||||
${thing.srtopbar.render()}
|
||||
%endif
|
||||
|
||||
${thing.srtopbar}
|
||||
<div id="header-bottom-${'right' if c.lang_rtl else 'left'}">
|
||||
<%
|
||||
if c.site.header and c.allow_styles:
|
||||
@@ -47,7 +44,7 @@
|
||||
|
||||
|
||||
%for toolbar in thing.toolbars:
|
||||
${toolbar.render()}
|
||||
${toolbar}
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
@@ -83,7 +80,7 @@
|
||||
${separator("|")}
|
||||
%endif
|
||||
|
||||
${thing.corner_buttons().render()}
|
||||
${thing.corner_buttons()}
|
||||
%if c.user_is_loggedin:
|
||||
${separator("|")}
|
||||
${logout()}
|
||||
|
||||
@@ -29,6 +29,6 @@
|
||||
<div id="header">
|
||||
<a href="/"><img id="header-img" src="${static('littlehead.png')}" alt="${c.site.name}" /></a><a href="/reddits" class="or">other reddits</a>
|
||||
%for toolbar in thing.toolbars:
|
||||
${toolbar.render()}
|
||||
${toolbar}
|
||||
%endfor
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
<body onclick="close_menus()" class="min-body">
|
||||
%if thing.content:
|
||||
<div class="content">
|
||||
${thing.content().render()}
|
||||
${thing.content()}
|
||||
</div>
|
||||
%endif
|
||||
</body>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<h4 style="color:gray">${thing.header}</h4>
|
||||
|
||||
<div id="previoussearch">
|
||||
${SearchForm(prev_search = thing.prev_search).render()}
|
||||
${SearchForm(prev_search = thing.prev_search)}
|
||||
</div>
|
||||
|
||||
%if thing.prev_search:
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
</span>
|
||||
</th>
|
||||
<td>
|
||||
<input class="real-name" value="${c.user.name}"
|
||||
<input class="real-name" value="${thing.username}"
|
||||
type="text" id="share_from_${thing.link_name}"
|
||||
name="share_from" />
|
||||
</td>
|
||||
@@ -57,7 +57,7 @@
|
||||
<td>
|
||||
<input name="replyto" type="text" size="30"
|
||||
id="replyto_${thing.link_name}"
|
||||
value="${c.user.email if hasattr(c.user, 'email') else ''}"/>
|
||||
value="${thing.email}"/>
|
||||
</td>
|
||||
<td class="reply-to-errors">
|
||||
${error_field("BAD_EMAILS", "replyto")}
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
%>
|
||||
<%inherit file="printable.html"/>
|
||||
|
||||
<%namespace file="printablebuttons.html" import="toggle_button"/>
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
<%namespace file="printable.html" import="toggle_button"/>
|
||||
|
||||
<%def name="numcol()">
|
||||
</%def>
|
||||
@@ -47,10 +47,10 @@
|
||||
</p>
|
||||
%endif
|
||||
<p class="tagline">
|
||||
${tagline()}
|
||||
${self.tagline()}
|
||||
</p>
|
||||
<ul class="flat-list buttons">
|
||||
${buttons()}
|
||||
${self.buttons()}
|
||||
</ul>
|
||||
</%def>
|
||||
|
||||
@@ -108,8 +108,3 @@
|
||||
<%def name="child()">
|
||||
</%def>
|
||||
|
||||
<%def name="buttons()">
|
||||
${parent.delete_or_report_buttons(delete=False)}
|
||||
${parent.buttons()}
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -21,41 +21,41 @@
|
||||
################################################################################
|
||||
|
||||
<%!
|
||||
from r2.models import FakeSubreddit
|
||||
from r2.lib.utils import timesince
|
||||
from r2.lib.strings import strings
|
||||
%>
|
||||
<%namespace file="printable.html" import="ynbutton, toggle_button, state_button"/>
|
||||
<%namespace file="printablebuttons.html"
|
||||
import="ynbutton, toggle_button, state_button"/>
|
||||
<%namespace file="printable.html" import="thing_css_class" />
|
||||
|
||||
%if not isinstance(c.site, FakeSubreddit):
|
||||
<div class="raisedbox subreddit-info ${thing_css_class(c.site)}">
|
||||
<h3>${c.site.name}</h3>
|
||||
%if c.user_is_loggedin:
|
||||
%if not thing.is_fake:
|
||||
<div class="raisedbox subreddit-info thing id-${thing.fullname}">
|
||||
<h3>${thing.name}</h3>
|
||||
%if thing.is_loggedin:
|
||||
${toggle_button("subscribe-button",
|
||||
_("subscribe"), _("unsubscribe"),
|
||||
'subscribe("%s")' % c.site._fullname,
|
||||
'unsubscribe("%s")' % c.site._fullname,
|
||||
reverse = c.site.is_subscriber_defaults(c.user))}
|
||||
'subscribe("%s")' % thing.fullname,
|
||||
'unsubscribe("%s")' % thing.fullname,
|
||||
reverse = thing.is_subscriber)}
|
||||
%endif
|
||||
<span class="label">
|
||||
(${strings.person_label % dict(num = c.site._ups,
|
||||
persons = ungettext('subscriber', 'subscribers', c.site._ups))})
|
||||
(${strings.person_label % dict(num = thing.subscribers,
|
||||
persons = ungettext('subscriber', 'subscribers',
|
||||
thing.subscribers))})
|
||||
</span>
|
||||
<p>
|
||||
${_("a community for %(time)s") % dict(time=timesince(c.site._date))}
|
||||
${_("a community for %(time)s") % dict(time=timesince(thing.date))}
|
||||
</p>
|
||||
|
||||
<div id="${c.site._fullname}" class="thing">
|
||||
%if c.site.type in ("private", "restricted") and c.user_is_loggedin and \
|
||||
c.site.is_contributor(c.user):
|
||||
<div id="${thing.fullname}" class="thing">
|
||||
%if thing.is_contributor:
|
||||
${moderate_button("leave_contributor",
|
||||
_("you are a contributor of this reddit. (%(leave)s)"),
|
||||
_("stop being a contributor?"),
|
||||
_("you are no longer a contributor"))}
|
||||
%endif
|
||||
|
||||
%if c.user_is_loggedin and c.site.is_moderator(c.user):
|
||||
%if thing.is_moderator:
|
||||
${moderate_button("leave_moderator",
|
||||
_("you are a moderator of this reddit. (%(leave)s)"),
|
||||
_("stop being a moderator?"),
|
||||
@@ -63,12 +63,12 @@
|
||||
|
||||
%endif
|
||||
</div>
|
||||
%if c.user_is_admin:
|
||||
%if c.site._spam:
|
||||
%if thing.is_admin:
|
||||
%if thing.spam:
|
||||
${state_button("unban", _("unban"),
|
||||
"return change_state(this, 'unban');", _("unbanned"))}
|
||||
%if hasattr(c.site, 'banner'):
|
||||
<div>${strings.banned_by % c.site.banner}</div>
|
||||
%if thing.banner:
|
||||
<div>${strings.banned_by % thing.banner}</div>
|
||||
%endif
|
||||
%else:
|
||||
${state_button("ban", _("ban"),
|
||||
@@ -77,7 +77,7 @@
|
||||
%endif
|
||||
<div class="spacer">
|
||||
%for n in thing.nav():
|
||||
${n.render()}
|
||||
${n}
|
||||
%endfor
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field, image_upload"/>
|
||||
<%namespace file="printable.html" import="ynbutton, toggle_button"/>
|
||||
<%namespace file="printablebuttons.html" import="ynbutton, toggle_button"/>
|
||||
|
||||
<div class="stylesheet-customize-container">
|
||||
<form
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<div id="sr-header-area">
|
||||
${thing.sr_dropdown.render()}
|
||||
${thing.sr_bar.render()}
|
||||
${thing.my_reddits_dropdown()}
|
||||
${thing.recent_reddits()}
|
||||
${plain_link(unsafe(_("more") + " »"), "/reddits/", id="sr-more-link")}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
${thing.tabmenu.render()}
|
||||
${thing.tabmenu}
|
||||
<div class="tabpane-content">
|
||||
%for i, (name, title, pane) in enumerate(thing.tabs):
|
||||
<div id="tabbedpane-${name}" class="tabbedpane"
|
||||
${"style='display:none'" if i > 0 else ""}>
|
||||
${pane.render()}
|
||||
${pane}
|
||||
</div>
|
||||
%endfor
|
||||
##select the front tab and init the other tabs
|
||||
|
||||
@@ -161,7 +161,7 @@ $(function() {
|
||||
%endif
|
||||
<table style="border-spacing: 5px">
|
||||
%endif
|
||||
${s.render()}
|
||||
${s}
|
||||
%endfor
|
||||
</table>
|
||||
<form>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<table>
|
||||
%if thing.users:
|
||||
%for item in thing.users:
|
||||
${item.render()}
|
||||
${item}
|
||||
%endfor
|
||||
%else:
|
||||
<tr><td></td></tr>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="printable.html" import="ynbutton" />
|
||||
<%namespace file="printablebuttons.html" import="ynbutton" />
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<tr>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
from r2.lib.utils import randstr
|
||||
%>
|
||||
|
||||
<%namespace file="printable.html" import="toggle_button" />
|
||||
<%namespace file="printablebuttons.html" import="toggle_button" />
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
<%def name="action_button(name, btn_type, onclick, display)">
|
||||
@@ -38,14 +38,13 @@
|
||||
<form action="#" class="${thing.css_class}"
|
||||
onsubmit="return post_form(this, '${thing.post_form}')"
|
||||
${"style='display:none'" if not thing.display else ""}
|
||||
id="form-${(thing.item._fullname if thing.item else "") + randstr(3)}">
|
||||
id="form-${thing.fullname + randstr(3)}">
|
||||
%else:
|
||||
<div class="${thing.css_class}">
|
||||
%endif
|
||||
|
||||
##this is set for both editting selftext and creating new comments
|
||||
<input type="hidden" name="thing_id"
|
||||
value="${thing.item and thing.item._fullname or ''}"/>
|
||||
<input type="hidden" name="thing_id" value="${thing.fullname}"/>
|
||||
|
||||
%if not thing.creating:
|
||||
<div class="usertext-body">
|
||||
|
||||
@@ -114,6 +114,13 @@ ${first_defined(kw[1:])}
|
||||
</%def>
|
||||
|
||||
<%def name="plain_link(link_text, path, _sr_path = True, nocname=False, fmt='', target='', **kw)">
|
||||
## caching comment:
|
||||
## in addition to the args, this function also makes use of c.cname,
|
||||
## and, via add_sr, both c.site.name and c.render_style.
|
||||
##
|
||||
## This function is called by (among other places) NavMenu as the
|
||||
## primary rendering view. Any changes to the c-usage of this function
|
||||
## will have to be propagated up.
|
||||
<%
|
||||
if (not target or target == '_parent') and c.cname:
|
||||
target = '_top'
|
||||
@@ -122,7 +129,9 @@ ${first_defined(kw[1:])}
|
||||
if target:
|
||||
kw['target'] = target
|
||||
|
||||
link = _a_buffered(link_text, href=add_sr(path, sr_path=_sr_path, nocname=nocname), **kw)
|
||||
link = _a_buffered(link_text,
|
||||
href=add_sr(path, sr_path=_sr_path, nocname=nocname),
|
||||
**kw)
|
||||
%>
|
||||
|
||||
${unsafe((fmt % link) if fmt else link)}
|
||||
|
||||
Reference in New Issue
Block a user