A new submit page.
The ability to submit to any subreddit from the regular submit page. The ability to add text to a self-post. Improved comments pages. Support for HD Youtube videos. Numerous bug-fixes.
@@ -61,9 +61,9 @@ def load_environment(global_conf={}, app_conf={}):
|
||||
#tmpl_options['myghty.escapes'] = dict(l=webhelpers.auto_link, s=webhelpers.simple_format)
|
||||
|
||||
tmpl_options = config['buffet.template_options']
|
||||
tmpl_options['mako.default_filters'] = ["websafe"]
|
||||
tmpl_options['mako.default_filters'] = ["mako_websafe"]
|
||||
tmpl_options['mako.imports'] = \
|
||||
["from r2.lib.filters import websafe, unsafe",
|
||||
["from r2.lib.filters import websafe, unsafe, mako_websafe",
|
||||
"from pylons import c, g, request",
|
||||
"from pylons.i18n import _, ungettext"]
|
||||
|
||||
|
||||
@@ -43,11 +43,12 @@ from r2.lib.menus import CommentSortMenu
|
||||
from r2.lib.normalized_hot import expire_hot
|
||||
from r2.lib.captcha import get_iden
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.filters import _force_unicode, websafe_json, spaceCompress
|
||||
from r2.lib.filters import _force_unicode, websafe_json, websafe, spaceCompress
|
||||
from r2.lib.db import queries
|
||||
from r2.lib.media import force_thumbnail, thumbnail_url
|
||||
from r2.lib.comment_tree import add_comment, delete_comment
|
||||
from r2.lib import tracking, sup, cssfilter, emailer
|
||||
from r2.lib.subreddit_search import search_reddits
|
||||
|
||||
from simplejson import dumps
|
||||
|
||||
@@ -89,28 +90,24 @@ class ApiController(RedditController):
|
||||
|
||||
@validatedForm(VCaptcha(),
|
||||
name=VRequired('name', errors.NO_NAME),
|
||||
email=VRequired('email', errors.NO_EMAIL),
|
||||
replyto = ValidEmails("replyto", num = 1),
|
||||
email=ValidEmails('email', num = 1),
|
||||
reason = VOneOf('reason', ('ad_inq', 'feedback')),
|
||||
message=VRequired('message', errors.NO_MESSAGE),
|
||||
message=VRequired('text', errors.NO_TEXT),
|
||||
)
|
||||
def POST_feedback(self, form, jquery, name, email,
|
||||
replyto, reason, message):
|
||||
def POST_feedback(self, form, jquery, name, email, reason, message):
|
||||
if not (form.has_errors('name', errors.NO_NAME) or
|
||||
form.has_errors('email', errors.NO_EMAIL) or
|
||||
(request.POST.get("replyto") and replyto is None and
|
||||
form.has_errors("replyto", errors.BAD_EMAILS)) or
|
||||
form.has_errors('personal', errors.NO_MESSAGE) or
|
||||
form.chk_captcha(errors.BAD_CAPTCHA)):
|
||||
form.has_errors('email', errors.BAD_EMAILS) or
|
||||
form.has_errors('text', errors.NO_TEXT) or
|
||||
form.has_errors('captcha', errors.BAD_CAPTCHA)):
|
||||
|
||||
if reason != 'ad_inq':
|
||||
emailer.feedback_email(email, message, name = name or '',
|
||||
reply_to = replyto or '')
|
||||
emailer.feedback_email(email, message, name, reply_to = '')
|
||||
else:
|
||||
emailer.ad_inq_email(email, message, name = name or '',
|
||||
reply_to = replyto or '')
|
||||
emailer.ad_inq_email(email, message, name, reply_to = '')
|
||||
|
||||
form.set_html(".status", _("thanks for your message! "
|
||||
"you should hear back from us shortly."))
|
||||
form.set_inputs(personal = "", captcha = "")
|
||||
form.set_inputs(text = "", captcha = "")
|
||||
|
||||
POST_ad_inq = POST_feedback
|
||||
|
||||
@@ -121,7 +118,7 @@ class ApiController(RedditController):
|
||||
ip = ValidIP(),
|
||||
to = VExistingUname('to'),
|
||||
subject = VRequired('subject', errors.NO_SUBJECT),
|
||||
body = VMessage('message'))
|
||||
body = VMessage(['text', 'message']))
|
||||
def POST_compose(self, form, jquery, to, subject, body, ip):
|
||||
"""
|
||||
handles message composition under /message/compose.
|
||||
@@ -129,16 +126,15 @@ class ApiController(RedditController):
|
||||
if not (form.has_errors("to", errors.USER_DOESNT_EXIST,
|
||||
errors.NO_USER) or
|
||||
form.has_errors("subject", errors.NO_SUBJECT) or
|
||||
form.has_errors("message", errors.NO_MSG_BODY,
|
||||
errors.COMMENT_TOO_LONG) or
|
||||
form.chk_captcha(errors.BAD_CAPTCHA)):
|
||||
form.has_errors("text", errors.NO_TEXT, errors.TOO_LONG) or
|
||||
form.has_errors("captcha", errors.BAD_CAPTCHA)):
|
||||
spam = (c.user._spam or
|
||||
errors.BANNED_IP in c.errors or
|
||||
errors.BANNED_DOMAIN in c.errors)
|
||||
|
||||
m, inbox_rel = Message._new(c.user, to, subject, body, ip, spam)
|
||||
form.set_html(".success", _("your message has been delivered"))
|
||||
form.set_inputs(to = "", subject = "", message = "")
|
||||
form.set_html(".status", _("your message has been delivered"))
|
||||
form.set_inputs(to = "", subject = "", text = "", captcha="")
|
||||
|
||||
if g.write_query_queue:
|
||||
queries.new_message(m, inbox_rel)
|
||||
@@ -155,39 +151,57 @@ class ApiController(RedditController):
|
||||
url = VUrl(['url', 'sr']),
|
||||
title = VTitle('title'),
|
||||
save = VBoolean('save'),
|
||||
then = VOneOf('then', ('tb', 'comments'), default='comments')
|
||||
)
|
||||
def POST_submit(self, form, jquery, url, title, save, sr, ip, then):
|
||||
selftext = VSelfText('text'),
|
||||
kind = VOneOf('kind', ['link', 'self', 'poll']),
|
||||
then = VOneOf('then', ('tb', 'comments'), default='comments'))
|
||||
def POST_submit(self, form, jquery, url, selftext, kind, title, save,
|
||||
sr, ip, then):
|
||||
#backwards compatability
|
||||
if url == 'self':
|
||||
kind = 'self'
|
||||
|
||||
if isinstance(url, (unicode, str)):
|
||||
# VUrl may have replaced 'url' by adding 'http://'
|
||||
form.set_inputs(url = url)
|
||||
|
||||
should_ratelimit = sr.should_ratelimit(c.user, 'link')
|
||||
|
||||
#remove the ratelimit error if the user's karma is high
|
||||
if not should_ratelimit:
|
||||
c.errors.remove(errors.RATELIMIT)
|
||||
if not kind:
|
||||
# this should only happen if somebody is trying to post
|
||||
# links in some automated manner outside of the regular
|
||||
# submission page, and hasn't updated their script
|
||||
return
|
||||
|
||||
# check for no url, or clear that error field on return
|
||||
if form.has_errors("url", errors.NO_URL, errors.BAD_URL):
|
||||
pass
|
||||
elif form.has_errors("url", errors.ALREADY_SUB):
|
||||
form.redirect(url[0].already_submitted_link)
|
||||
# check for title, otherwise look it up and return it
|
||||
elif form.has_errors("title", errors.NO_TITLE):
|
||||
# try to fetch the title
|
||||
title = get_title(url)
|
||||
if title:
|
||||
# note: focus first, since it clears the form
|
||||
form.set_inputs(title = title)
|
||||
# wipe out the no title error
|
||||
form.clear_errors(errors.NO_TITLE)
|
||||
return
|
||||
if form.has_errors('sr', errors.SUBREDDIT_NOEXIST,
|
||||
errors.SUBREDDIT_REQUIRED):
|
||||
# checking to get the error set in the form, but we can't
|
||||
# check for rate-limiting if there's no subreddit
|
||||
return
|
||||
else:
|
||||
should_ratelimit = sr.should_ratelimit(c.user, 'link')
|
||||
#remove the ratelimit error if the user's karma is high
|
||||
if not should_ratelimit:
|
||||
c.errors.remove((errors.RATELIMIT, 'ratelimit'))
|
||||
|
||||
elif (form.has_errors("title", errors.TITLE_TOO_LONG) or
|
||||
form.chk_captcha(errors.BAD_CAPTCHA, errors.RATELIMIT)):
|
||||
if kind == 'link':
|
||||
# check for no url, or clear that error field on return
|
||||
if form.has_errors("url", errors.NO_URL, errors.BAD_URL):
|
||||
pass
|
||||
elif form.has_errors("url", errors.ALREADY_SUB):
|
||||
form.redirect(url[0].already_submitted_link)
|
||||
# check for title, otherwise look it up and return it
|
||||
elif form.has_errors("title", errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
elif kind == 'self' and form.has_errors('text', errors.TOO_LONG):
|
||||
pass
|
||||
|
||||
if form.has_error() or not title: return
|
||||
if form.has_errors("title", errors.TOO_LONG, errors.NO_TEXT):
|
||||
pass
|
||||
|
||||
if form.has_errors('ratelimit', errors.RATELIMIT):
|
||||
pass
|
||||
|
||||
if form.has_error() or not title:
|
||||
return
|
||||
|
||||
# check whether this is spam:
|
||||
spam = (c.user._spam or
|
||||
@@ -195,12 +209,17 @@ class ApiController(RedditController):
|
||||
errors.BANNED_DOMAIN in c.errors)
|
||||
|
||||
# well, nothing left to do but submit it
|
||||
l = Link._submit(request.post.title, url, c.user, sr, ip, spam)
|
||||
if url.lower() == 'self':
|
||||
l = Link._submit(request.post.title, url if kind == 'link' else 'self',
|
||||
c.user, sr, ip, spam)
|
||||
|
||||
if kind == 'self':
|
||||
l.url = l.make_permalink_slow()
|
||||
l.is_self = True
|
||||
l.selftext = selftext
|
||||
|
||||
l._commit()
|
||||
l.set_url_cache()
|
||||
|
||||
v = Vote.vote(c.user, l, True, ip, spam)
|
||||
if save:
|
||||
r = l._save(c.user)
|
||||
@@ -240,6 +259,18 @@ class ApiController(RedditController):
|
||||
|
||||
form.redirect(path)
|
||||
|
||||
|
||||
@validatedForm(VUser(),
|
||||
url = VSanitizedUrl(['url']))
|
||||
def POST_fetch_title(self, form, jquery, url):
|
||||
if url:
|
||||
title = get_title(url)
|
||||
if title:
|
||||
form.set_inputs(title = title)
|
||||
form.set_html(".title-status", "");
|
||||
else:
|
||||
form.set_html(".title-status", _("no title found"))
|
||||
|
||||
def _login(self, form, user, dest='', rem = None):
|
||||
"""
|
||||
AJAX login handler, used by both login and register to set the
|
||||
@@ -282,7 +313,9 @@ class ApiController(RedditController):
|
||||
form.has_errors("email", errors.BAD_EMAILS) or
|
||||
form.has_errors("passwd", errors.BAD_PASSWORD) or
|
||||
form.has_errors("passwd2", errors.BAD_PASSWORD_MATCH) or
|
||||
form.chk_captcha(errors.BAD_CAPTCHA,errors.RATELIMIT)):
|
||||
form.has_errors('ratelimit', errors.RATELIMIT) or
|
||||
form.has_errors('captcha', errors.BAD_CAPTCHA)):
|
||||
|
||||
user = register(name, password)
|
||||
VRatelimit.ratelimit(rate_ip = True, prefix = "rate_register_")
|
||||
|
||||
@@ -310,7 +343,6 @@ class ApiController(RedditController):
|
||||
self._subscribe(sr, sub)
|
||||
|
||||
self._login(form, user, dest, rem)
|
||||
|
||||
|
||||
@noresponse(VUser(),
|
||||
VModhash(),
|
||||
@@ -429,7 +461,6 @@ class ApiController(RedditController):
|
||||
"""
|
||||
# password is required to proceed
|
||||
if form.has_errors("curpass", errors.WRONG_PASSWORD):
|
||||
form.set_input(curpass = "")
|
||||
return
|
||||
|
||||
# check if the email is valid. If one is given and it is
|
||||
@@ -444,7 +475,7 @@ class ApiController(RedditController):
|
||||
updated = True
|
||||
|
||||
# change password
|
||||
if (password and
|
||||
if (password and
|
||||
not (form.has_errors("newpass", errors.BAD_PASSWORD) or
|
||||
form.has_errors("verpass", errors.BAD_PASSWORD_MATCH))):
|
||||
change_password(c.user, password)
|
||||
@@ -454,6 +485,7 @@ class ApiController(RedditController):
|
||||
else:
|
||||
form.set_html('.status',
|
||||
_('your password has been updated'))
|
||||
form.set_inputs(curpass = "", newpass = "", verpass = "")
|
||||
# the password has changed, so the user's cookie has been
|
||||
# invalidated. drop a new cookie.
|
||||
self.login(c.user)
|
||||
@@ -509,32 +541,41 @@ class ApiController(RedditController):
|
||||
Report.new(c.user, thing)
|
||||
|
||||
|
||||
@validatedForm(VUser(), VModhash(),
|
||||
comment = VByNameIfAuthor('parent'),
|
||||
body = VComment('comment'))
|
||||
def POST_editcomment(self, form, jquery, comment, body):
|
||||
|
||||
if not form.has_errors("comment",
|
||||
errors.BAD_COMMENT, errors.COMMENT_TOO_LONG,
|
||||
@validatedForm(VUser(),
|
||||
VModhash(),
|
||||
item = VByNameIfAuthor('thing_id'),
|
||||
text = VComment('text'))
|
||||
def POST_editusertext(self, form, jquery, item, text):
|
||||
if not form.has_errors("text",
|
||||
errors.NO_TEXT, errors.TOO_LONG,
|
||||
errors.NOT_AUTHOR):
|
||||
comment.body = body
|
||||
comment.editted = True
|
||||
comment._commit()
|
||||
|
||||
jquery.replace_things(comment, True, True)
|
||||
if isinstance(item, Comment):
|
||||
kind = 'comment'
|
||||
item.body = text
|
||||
elif isinstance(item, Link):
|
||||
kind = 'link'
|
||||
item.selftext = text
|
||||
|
||||
# flag search indexer that something has changed
|
||||
tc.changed(comment)
|
||||
item.editted = True
|
||||
item._commit()
|
||||
|
||||
tc.changed(item)
|
||||
|
||||
if kind == 'link':
|
||||
set_last_modified(item, 'comments')
|
||||
|
||||
wrapper = make_wrapper(ListingController.builder_wrapper,
|
||||
expand_children = True)
|
||||
jquery(".content").replace_things(item, True, True, wrap = wrapper)
|
||||
|
||||
@validatedForm(VUser(),
|
||||
VModhash(),
|
||||
VRatelimit(rate_user = True, rate_ip = True,
|
||||
prefix = "rate_comment_"),
|
||||
ip = ValidIP(),
|
||||
parent = VSubmitParent('parent'),
|
||||
comment = VComment('comment'))
|
||||
VModhash(),
|
||||
VRatelimit(rate_user = True, rate_ip = True,
|
||||
prefix = "rate_comment_"),
|
||||
ip = ValidIP(),
|
||||
parent = VSubmitParent(['thing_id', 'parent']),
|
||||
comment = VComment(['text', 'comment']))
|
||||
def POST_comment(self, commentform, jquery, parent, comment, ip):
|
||||
should_ratelimit = True
|
||||
#check the parent type here cause we need that for the
|
||||
@@ -559,14 +600,14 @@ class ApiController(RedditController):
|
||||
if not should_ratelimit:
|
||||
c.errors.remove(errors.RATELIMIT)
|
||||
|
||||
if (not commentform.has_errors("comment",
|
||||
errors.BAD_COMMENT,
|
||||
errors.COMMENT_TOO_LONG,
|
||||
if (not commentform.has_errors("text",
|
||||
errors.NO_TEXT,
|
||||
errors.TOO_LONG,
|
||||
errors.RATELIMIT) and
|
||||
not commentform.has_errors("parent",
|
||||
errors.DELETED_COMMENT)):
|
||||
spam = (c.user._spam or errors.BANNED_IP in c.errors)
|
||||
|
||||
|
||||
if is_message:
|
||||
to = Account._byID(parent.author_id)
|
||||
subject = parent.subject
|
||||
@@ -607,7 +648,6 @@ class ApiController(RedditController):
|
||||
# remove any null listings that may be present
|
||||
jquery("#noresults").hide()
|
||||
|
||||
|
||||
#update the queries
|
||||
if g.write_query_queue:
|
||||
if is_message:
|
||||
@@ -627,10 +667,10 @@ class ApiController(RedditController):
|
||||
VCaptcha(),
|
||||
VRatelimit(rate_user = True, rate_ip = True,
|
||||
prefix = "rate_share_"),
|
||||
share_from = VLength('share_from', length = 100),
|
||||
share_from = VLength('share_from', max_length = 100),
|
||||
emails = ValidEmails("share_to"),
|
||||
reply_to = ValidEmails("replyto", num = 1),
|
||||
message = VLength("message", length = 1000),
|
||||
message = VLength("message", max_length = 1000),
|
||||
thing = VByName('parent'))
|
||||
def POST_share(self, shareform, jquery, emails, thing, share_from, reply_to,
|
||||
message):
|
||||
@@ -641,14 +681,11 @@ class ApiController(RedditController):
|
||||
if not should_ratelimit:
|
||||
c.errors.remove(errors.RATELIMIT)
|
||||
|
||||
if emails and (errors.NO_EMAILS in c.errors):
|
||||
c.errors.remove(errors.NO_EMAILS)
|
||||
|
||||
# share_from and messages share a comment_too_long error.
|
||||
# share_from and messages share a too_long error.
|
||||
# finding an error on one necessitates hiding the other error
|
||||
if shareform.has_errors("share_from", errors.COMMENT_TOO_LONG):
|
||||
if shareform.has_errors("share_from", errors.TOO_LONG):
|
||||
shareform.find(".message-errors").children().hide()
|
||||
elif shareform.has_errors("message", errors.COMMENT_TOO_LONG):
|
||||
elif shareform.has_errors("message", errors.TOO_LONG):
|
||||
shareform.find(".share-form-errors").children().hide()
|
||||
# reply_to and share_to also share errors...
|
||||
elif shareform.has_errors("share_to", errors.BAD_EMAILS,
|
||||
@@ -656,11 +693,12 @@ class ApiController(RedditController):
|
||||
errors.TOO_MANY_EMAILS):
|
||||
shareform.find(".reply-to-errors").children().hide()
|
||||
elif shareform.has_errors("replyto", errors.BAD_EMAILS,
|
||||
errors.NO_EMAILS,
|
||||
errors.TOO_MANY_EMAILS):
|
||||
shareform.find(".share-to-errors").children().hide()
|
||||
# lastly, check the captcha.
|
||||
elif shareform.chk_captcha(errors.BAD_CAPTCHA, errors.RATELIMIT):
|
||||
elif shareform.has_errors("captcha", errors.BAD_CAPTCHA):
|
||||
pass
|
||||
elif shareform.has_errors("ratelimit", errors.RATELIMIT):
|
||||
pass
|
||||
else:
|
||||
c.user.add_share_emails(emails)
|
||||
@@ -845,7 +883,7 @@ class ApiController(RedditController):
|
||||
|
||||
@validate(VSrModerator(),
|
||||
VModhash(),
|
||||
file = VLength('file', length=1024*500),
|
||||
file = VLength('file', max_length=1024*500),
|
||||
name = VCssName("name"),
|
||||
header = nop('header'))
|
||||
def POST_upload_sr_img(self, file, header, name):
|
||||
@@ -910,9 +948,9 @@ class ApiController(RedditController):
|
||||
prefix = 'create_reddit_'),
|
||||
sr = VByName('sr'),
|
||||
name = VSubredditName("name"),
|
||||
title = VSubredditTitle("title"),
|
||||
title = VLength("title", max_length = 100),
|
||||
domain = VCnameDomain("domain"),
|
||||
description = VSubredditDesc("description"),
|
||||
description = VLength("description", max_length = 500),
|
||||
lang = VLang("lang"),
|
||||
over_18 = VBoolean('over_18'),
|
||||
show_media = VBoolean('show_media'),
|
||||
@@ -946,12 +984,12 @@ class ApiController(RedditController):
|
||||
elif not sr and form.has_errors("name", errors.SUBREDDIT_EXISTS,
|
||||
errors.BAD_SR_NAME):
|
||||
form.find('#example_name').hide()
|
||||
elif form.has_errors('title', errors.NO_TITLE, errors.TITLE_TOO_LONG):
|
||||
elif form.has_errors('title', errors.NO_TEXT, errors.TOO_LONG):
|
||||
form.find('#example_title').hide()
|
||||
elif form.has_errors('domain', errors.BAD_CNAME, errors.USED_CNAME):
|
||||
form.find('#example_domain').hide()
|
||||
elif (form.has_errors(None, errors.INVALID_OPTION) or
|
||||
form.has_errors('description', errors.DESC_TOO_LONG)):
|
||||
form.has_errors('description', errors.TOO_LONG)):
|
||||
pass
|
||||
#creating a new reddit
|
||||
elif not sr:
|
||||
@@ -1141,8 +1179,11 @@ class ApiController(RedditController):
|
||||
|
||||
@validatedForm(user = VUserWithEmail('name'))
|
||||
def POST_password(self, form, jquery, user):
|
||||
if not form.has_errors('name', errors.USER_DOESNT_EXIST,
|
||||
errors.NO_EMAIL_FOR_USER):
|
||||
if form.has_errors('name', errors.USER_DOESNT_EXIST):
|
||||
return
|
||||
elif form.has_errors('name', errors.NO_EMAIL_FOR_USER):
|
||||
return
|
||||
else:
|
||||
emailer.password_email(user)
|
||||
form.set_html(".status", _("an email will be sent to that account's address shortly"))
|
||||
|
||||
@@ -1150,16 +1191,17 @@ class ApiController(RedditController):
|
||||
@validatedForm(cache_evt = VCacheKey('reset', ('key', 'name')),
|
||||
password = VPassword(['passwd', 'passwd2']))
|
||||
def POST_resetpassword(self, form, jquery, cache_evt, password):
|
||||
if errors.BAD_USERNAME in c.errors:
|
||||
# clear reset event -- the user failed to know their user name
|
||||
if form.has_errors('name', errors.EXPIRED):
|
||||
cache_evt.clear()
|
||||
return form.redirect('/password')
|
||||
elif (not form.has_errors('passwd', errors.BAD_PASSWORD) and
|
||||
not form.has_errors('passwd2', errors.BAD_PASSWORD_MATCH) and
|
||||
cache_evt.user):
|
||||
form.redirect('/password')
|
||||
elif form.has_errors('passwd', errors.BAD_PASSWORD):
|
||||
pass
|
||||
elif form.has_errors('passwd2', errors.BAD_PASSWORD_MATCH):
|
||||
pass
|
||||
elif cache_evt.user:
|
||||
# successfully entered user name and valid new password
|
||||
change_password(cache_evt.user, password)
|
||||
self._login(jquery, cache_evt.user, '/resetpassword')
|
||||
self._login(jquery, cache_evt.user, '/')
|
||||
cache_evt.clear()
|
||||
|
||||
|
||||
@@ -1239,7 +1281,7 @@ class ApiController(RedditController):
|
||||
l.num_margin = 0
|
||||
l.mid_margin = 0
|
||||
|
||||
jquery.replace_things(l, stubs = True)
|
||||
jquery(".content").replace_things(l, stubs = True)
|
||||
|
||||
if show:
|
||||
jquery('.organic-listing .link:visible').hide()
|
||||
@@ -1289,7 +1331,7 @@ class ApiController(RedditController):
|
||||
# we're allowing mutliple submissions, so we really just
|
||||
# want the URL
|
||||
url = url[0].url
|
||||
if form.has_errors('title', errors.NO_TITLE):
|
||||
if form.has_errors('title', errors.NO_TEXT, errors.TOO_LONG):
|
||||
pass
|
||||
elif form.has_errors('url', errors.NO_URL, errors.BAD_URL):
|
||||
pass
|
||||
@@ -1345,7 +1387,7 @@ class ApiController(RedditController):
|
||||
|
||||
@validate(VSponsor(),
|
||||
link = VByName('link_id'),
|
||||
file = VLength('file',500*1024))
|
||||
file = VLength('file', 500*1024))
|
||||
def POST_link_thumb(self, link=None, file=None):
|
||||
errors = dict(BAD_CSS_NAME = "", IMAGE_ERROR = "")
|
||||
try:
|
||||
@@ -1407,3 +1449,18 @@ class ApiController(RedditController):
|
||||
ip = request.ip)
|
||||
)
|
||||
|
||||
@json_validate(query = nop('query'))
|
||||
def POST_search_reddit_names(self, query):
|
||||
names = []
|
||||
if query:
|
||||
names = search_reddits(query)
|
||||
|
||||
return {'names': names}
|
||||
|
||||
@validate(link = VByName('link_id', thing_cls = Link))
|
||||
def POST_expando(self, link):
|
||||
if not link:
|
||||
abort(404, 'not found')
|
||||
|
||||
wrapped = IDBuilder([link._fullname]).get_items()[0][0]
|
||||
return spaceCompress(websafe(wrapped.link_child.content()))
|
||||
|
||||
@@ -25,17 +25,13 @@ from copy import copy
|
||||
|
||||
error_list = dict((
|
||||
('USER_REQUIRED', _("please login to do that")),
|
||||
('NO_URL', _('url required')),
|
||||
('NO_URL', _('a url is required')),
|
||||
('BAD_URL', _('you should check that url')),
|
||||
('NO_TITLE', _('title required')),
|
||||
('TITLE_TOO_LONG', _('you can be more succinct than that')),
|
||||
('COMMENT_TOO_LONG', _('you can be more succinct than that')),
|
||||
('BAD_CAPTCHA', _('your letters stink')),
|
||||
('BAD_CAPTCHA', _('care to try these again?')),
|
||||
('BAD_USERNAME', _('invalid user name')),
|
||||
('USERNAME_TAKEN', _('that username is already taken')),
|
||||
('NO_THING_ID', _('id not specified')),
|
||||
('NOT_AUTHOR', _("you can't do that")),
|
||||
('BAD_COMMENT', _('please enter a comment')),
|
||||
('DELETED_COMMENT', _('that comment has been deleted')),
|
||||
('DELETED_THING', _('that element has been deleted.')),
|
||||
('BAD_PASSWORD', _('invalid password')),
|
||||
@@ -44,9 +40,7 @@ error_list = dict((
|
||||
('NO_NAME', _('please enter a name')),
|
||||
('NO_EMAIL', _('please enter an email address')),
|
||||
('NO_EMAIL_FOR_USER', _('no email address for that user')),
|
||||
('NO_MESSAGE', _('please enter a message')),
|
||||
('NO_TO_ADDRESS', _('send it to whom?')),
|
||||
('NO_MSG_BODY', _('please enter a message')),
|
||||
('NO_SUBJECT', _('please enter a subject')),
|
||||
('USER_DOESNT_EXIST', _("that user doesn't exist")),
|
||||
('NO_USER', _('please enter a username')),
|
||||
@@ -55,6 +49,7 @@ error_list = dict((
|
||||
('ALREADY_SUB', _("that link has already been submitted")),
|
||||
('SUBREDDIT_EXISTS', _('that reddit already exists')),
|
||||
('SUBREDDIT_NOEXIST', _('that reddit doesn\'t exist')),
|
||||
('SUBREDDIT_REQUIRED', _('you must specify a reddit')),
|
||||
('BAD_SR_NAME', _('that name isn\'t going to work')),
|
||||
('RATELIMIT', _('you are trying to submit too fast. try again in %(time)s.')),
|
||||
('EXPIRED', _('your session has expired')),
|
||||
@@ -64,11 +59,13 @@ error_list = dict((
|
||||
('BAD_CNAME', "that domain isn't going to work"),
|
||||
('USED_CNAME', "that domain is already in use"),
|
||||
('INVALID_OPTION', _('that option is not valid')),
|
||||
('DESC_TOO_LONG', _('description is too long')),
|
||||
('CHEATER', 'what do you think you\'re doing there?'),
|
||||
('BAD_EMAILS', _('the following emails are invalid: %(emails)s')),
|
||||
('NO_EMAILS', _('please enter at least one email address')),
|
||||
('TOO_MANY_EMAILS', _('please only share to %(num)s emails at a time.')),
|
||||
|
||||
('TOO_LONG', _("this is too long (max: %(max_length)s)")),
|
||||
('NO_TEXT', _('we need something here')),
|
||||
))
|
||||
errors = Storage([(e, e) for e in error_list.keys()])
|
||||
|
||||
@@ -97,8 +94,10 @@ class ErrorSet(object):
|
||||
def __init__(self):
|
||||
self.errors = {}
|
||||
|
||||
def __contains__(self, error_name):
|
||||
return self.errors.has_key(error_name)
|
||||
def __contains__(self, pair):
|
||||
"""Expectes an (error_name, field_name) tuple and checks to
|
||||
see if it's in the errors list."""
|
||||
return self.errors.has_key(pair)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return self.errors[name]
|
||||
@@ -110,16 +109,16 @@ class ErrorSet(object):
|
||||
for x in self.errors:
|
||||
yield x
|
||||
|
||||
def _add(self, error_name, msg, msg_params = {}, field = None):
|
||||
self.errors[error_name] = Error(error_name, msg, msg_params,
|
||||
field = field)
|
||||
|
||||
def add(self, error_name, msg_params = {}, field = None):
|
||||
msg = error_list[error_name]
|
||||
self._add(error_name, msg, msg_params = msg_params, field = field)
|
||||
for field_name in tup(field):
|
||||
e = Error(error_name, msg, msg_params, field = field_name)
|
||||
self.errors[(error_name, field_name)] = e
|
||||
|
||||
def remove(self, error_name):
|
||||
if self.errors.has_key(error_name):
|
||||
del self.errors[error_name]
|
||||
def remove(self, pair):
|
||||
"""Expectes an (error_name, field_name) tuple and removes it
|
||||
from the errors list."""
|
||||
if self.errors.has_key(pair):
|
||||
del self.errors[pair]
|
||||
|
||||
class UserRequiredException(Exception): pass
|
||||
|
||||
@@ -26,29 +26,16 @@ from r2.lib.pages import FormPage, Feedback, Captcha
|
||||
|
||||
class FeedbackController(RedditController):
|
||||
|
||||
def _feedback(self, name = '', email = '', message='',
|
||||
replyto='', action=''):
|
||||
title = _("inquire about advertising on reddit") if action else ''
|
||||
captcha = Captcha() if not c.user_is_loggedin \
|
||||
or c.user.needs_captcha() else None
|
||||
if request.get.has_key("done"):
|
||||
success = _("thanks for your message! you should hear back from us shortly.")
|
||||
else:
|
||||
success = ''
|
||||
return FormPage(_("advertise") if action == 'ad_inq' \
|
||||
else _("feedback"),
|
||||
content = Feedback(captcha=captcha,
|
||||
message=message,
|
||||
replyto=replyto,
|
||||
email=email, name=name,
|
||||
success=success,
|
||||
action=action,
|
||||
title=title),
|
||||
def GET_ad_inq(self):
|
||||
title = _("inquire about advertising on reddit")
|
||||
return FormPage('advertise',
|
||||
content = Feedback(title=title,
|
||||
action='ad_inq'),
|
||||
loginbox = False).render()
|
||||
|
||||
|
||||
def GET_ad_inq(self):
|
||||
return self._feedback(action='ad_inq')
|
||||
|
||||
def GET_feedback(self):
|
||||
return self._feedback()
|
||||
title = _("send reddit feedback")
|
||||
return FormPage('feedback',
|
||||
content = Feedback(title=title,
|
||||
action='feedback'),
|
||||
loginbox = False).render()
|
||||
|
||||
@@ -109,7 +109,7 @@ class FrontController(RedditController):
|
||||
if not key and request.referer:
|
||||
referer_path = request.referer.split(g.domain)[-1]
|
||||
done = referer_path.startswith(request.fullpath)
|
||||
elif not cache_evt.user:
|
||||
elif not getattr(cache_evt, "user", None):
|
||||
return self.abort404()
|
||||
return BoringPage(_("reset password"),
|
||||
content=ResetPassword(key=key, done=done)).render()
|
||||
@@ -174,12 +174,11 @@ class FrontController(RedditController):
|
||||
# insert reply box only for logged in user
|
||||
if c.user_is_loggedin and article.subreddit_slow.can_comment(c.user):
|
||||
#no comment box for permalinks
|
||||
if not comment:
|
||||
displayPane.append(CommentReplyBox(link_name =
|
||||
article._fullname))
|
||||
else:
|
||||
displayPane.append(CommentReplyBox())
|
||||
|
||||
displayPane.append(UserText(item = article, creating = True,
|
||||
post_form = 'comment',
|
||||
display = not bool(comment),
|
||||
cloneable = True))
|
||||
|
||||
# finally add the comment listing
|
||||
displayPane.append(listing.listing())
|
||||
|
||||
@@ -498,7 +497,9 @@ class FrontController(RedditController):
|
||||
return res
|
||||
|
||||
captcha = Captcha() if c.user.needs_captcha() else None
|
||||
sr_names = Subreddit.submit_sr_names(c.user) if c.default_sr else ()
|
||||
sr_names = (Subreddit.submit_sr_names(c.user) or
|
||||
Subreddit.submit_sr_names(None))
|
||||
|
||||
|
||||
return FormPage(_("submit"),
|
||||
content=NewLink(url=url or '',
|
||||
|
||||
@@ -48,12 +48,15 @@ class Validator(object):
|
||||
self.default = default
|
||||
self.post, self.get, self.url = post, get, url
|
||||
|
||||
def set_error(self, error, msg_params = {}):
|
||||
def set_error(self, error, msg_params = {}, field = False):
|
||||
"""
|
||||
Adds the provided error to c.errors and flags that it is come
|
||||
from the validator's param
|
||||
"""
|
||||
c.errors.add(error, msg_params = msg_params, field = self.param)
|
||||
if field is False:
|
||||
field = self.param
|
||||
|
||||
c.errors.add(error, msg_params = msg_params, field = field)
|
||||
|
||||
def __call__(self, url):
|
||||
a = []
|
||||
@@ -135,7 +138,7 @@ def api_validate(response_function):
|
||||
simple_vals, param_vals, *a, **kw)
|
||||
except UserRequiredException:
|
||||
responder.send_failure(errors.USER_REQUIRED)
|
||||
return self.response_func(dict(iter(responder)))
|
||||
return self.response_func(responder.make_response())
|
||||
return newfn
|
||||
return val
|
||||
return _api_validate
|
||||
@@ -146,6 +149,10 @@ def noresponse(self, self_method, responder, simple_vals, param_vals, *a, **kw):
|
||||
self_method(self, *a, **kw)
|
||||
return self.response_func({})
|
||||
|
||||
@api_validate
|
||||
def json_validate(self, self_method, responder, simple_vals, param_vals, *a, **kw):
|
||||
r = self_method(self, *a, **kw)
|
||||
return self.response_func(r)
|
||||
|
||||
@api_validate
|
||||
def validatedForm(self, self_method, responder, simple_vals, param_vals,
|
||||
@@ -156,16 +163,19 @@ def validatedForm(self, self_method, responder, simple_vals, param_vals,
|
||||
# clear out the status line as a courtesy
|
||||
form.set_html(".status", "")
|
||||
|
||||
# do the actual work
|
||||
val = self_method(self, form, responder, *a, **kw)
|
||||
|
||||
# auto-refresh the captcha if there are errors.
|
||||
if (c.errors.errors and
|
||||
any(isinstance(v, VCaptcha) for v in simple_vals)):
|
||||
form.has_errors('captcha', errors.BAD_CAPTCHA)
|
||||
form.new_captcha()
|
||||
|
||||
if val: return val
|
||||
return self.response_func(dict(iter(responder)))
|
||||
# do the actual work
|
||||
val = self_method(self, form, responder, *a, **kw)
|
||||
|
||||
if val:
|
||||
return val
|
||||
else:
|
||||
return self.response_func(responder.make_response())
|
||||
|
||||
|
||||
|
||||
@@ -266,46 +276,41 @@ def chksrname(x):
|
||||
|
||||
|
||||
class VLength(Validator):
|
||||
def __init__(self, item, length = 10000,
|
||||
empty_error = errors.BAD_COMMENT,
|
||||
length_error = errors.COMMENT_TOO_LONG, **kw):
|
||||
Validator.__init__(self, item, **kw)
|
||||
self.length = length
|
||||
self.len_error = length_error
|
||||
self.emp_error = empty_error
|
||||
only_whitespace = re.compile(r"^\s*$", re.UNICODE)
|
||||
|
||||
def run(self, title):
|
||||
if not title:
|
||||
self.set_error(self.emp_error)
|
||||
elif len(title) > self.length:
|
||||
self.set_error(self.len_error)
|
||||
def __init__(self, param, max_length,
|
||||
empty_error = errors.NO_TEXT,
|
||||
length_error = errors.TOO_LONG,
|
||||
**kw):
|
||||
Validator.__init__(self, param, **kw)
|
||||
self.max_length = max_length
|
||||
self.length_error = length_error
|
||||
self.empty_error = empty_error
|
||||
|
||||
def run(self, text, text2 = ''):
|
||||
text = text or text2
|
||||
if self.empty_error and (not text or self.only_whitespace.match(text)):
|
||||
self.set_error(self.empty_error)
|
||||
elif len(text) > self.max_length:
|
||||
self.set_error(self.length_error, {'max_length': self.max_length})
|
||||
else:
|
||||
return title
|
||||
return text
|
||||
|
||||
class VTitle(VLength):
|
||||
only_whitespace = re.compile(r"^\s*$", re.UNICODE)
|
||||
|
||||
def __init__(self, item, length = 300, **kw):
|
||||
VLength.__init__(self, item, length = length,
|
||||
empty_error = errors.NO_TITLE,
|
||||
length_error = errors.TITLE_TOO_LONG, **kw)
|
||||
|
||||
def run(self, title):
|
||||
title = VLength.run(self, title)
|
||||
if title and self.only_whitespace.match(title):
|
||||
self.set_error(errors.NO_TITLE)
|
||||
else:
|
||||
return title
|
||||
def __init__(self, param, max_length = 300, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VComment(VLength):
|
||||
def __init__(self, item, length = 10000, **kw):
|
||||
VLength.__init__(self, item, length = length, **kw)
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VSelfText(VLength):
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
class VMessage(VLength):
|
||||
def __init__(self, item, length = 10000, **kw):
|
||||
VLength.__init__(self, item, length = length,
|
||||
empty_error = errors.NO_MSG_BODY, **kw)
|
||||
def __init__(self, param, max_length = 10000, **kw):
|
||||
VLength.__init__(self, param, max_length, **kw)
|
||||
|
||||
|
||||
class VSubredditName(VRequired):
|
||||
@@ -388,7 +393,7 @@ class VByNameIfAuthor(VByName):
|
||||
if not thing._loaded: thing._load()
|
||||
if c.user_is_loggedin and thing.author_id == c.user._id:
|
||||
return thing
|
||||
return self.error(errors.NOT_AUTHOR)
|
||||
return self.set_error(errors.NOT_AUTHOR)
|
||||
|
||||
class VCaptcha(Validator):
|
||||
default_param = ('iden', 'captcha')
|
||||
@@ -466,7 +471,9 @@ class VSRSubmitPage(Validator):
|
||||
abort(403, "forbidden")
|
||||
|
||||
class VSubmitParent(VByName):
|
||||
def run(self, fullname):
|
||||
def run(self, fullname, fullname2):
|
||||
#for backwards compatability (with iphone app)
|
||||
fullname = fullname or fullname2
|
||||
if fullname:
|
||||
parent = VByName.run(self, fullname)
|
||||
if parent and parent._deleted:
|
||||
@@ -482,30 +489,34 @@ class VSubmitParent(VByName):
|
||||
|
||||
class VSubmitSR(Validator):
|
||||
def run(self, sr_name):
|
||||
if not sr_name:
|
||||
self.set_error(errors.SUBREDDIT_REQUIRED)
|
||||
return None
|
||||
|
||||
try:
|
||||
sr = Subreddit._by_name(sr_name)
|
||||
except (NotFound, AttributeError):
|
||||
self.set_error(errors.SUBREDDIT_NOEXIST)
|
||||
sr = None
|
||||
return None
|
||||
|
||||
if sr and not (c.user_is_loggedin and sr.can_submit(c.user)):
|
||||
abort(403, "forbidden")
|
||||
else:
|
||||
return sr
|
||||
|
||||
pass_rx = re.compile(r".{3,20}")
|
||||
pass_rx = re.compile(r"^.{3,20}$")
|
||||
|
||||
def chkpass(x):
|
||||
return x if x and pass_rx.match(x) else None
|
||||
|
||||
class VPassword(VRequired):
|
||||
def __init__(self, item, *a, **kw):
|
||||
VRequired.__init__(self, item, errors.BAD_PASSWORD, *a, **kw)
|
||||
class VPassword(Validator):
|
||||
def run(self, password, verify):
|
||||
if not chkpass(password):
|
||||
return self.error()
|
||||
self.set_error(errors.BAD_PASSWORD)
|
||||
return
|
||||
elif verify != password:
|
||||
return self.error(errors.BAD_PASSWORD_MATCH)
|
||||
self.set_error(errors.BAD_PASSWORD_MATCH)
|
||||
return password
|
||||
else:
|
||||
return password
|
||||
|
||||
@@ -695,11 +706,11 @@ class VRatelimit(Validator):
|
||||
# when errors have associated field parameters, we'll need
|
||||
# to add that here
|
||||
if self.error == errors.RATELIMIT:
|
||||
self.set_error(errors.RATELIMIT, {'time': time})
|
||||
self.set_error(errors.RATELIMIT, {'time': time},
|
||||
field = 'ratelimit')
|
||||
else:
|
||||
self.set_error(self.error)
|
||||
|
||||
|
||||
@classmethod
|
||||
def ratelimit(self, rate_user = False, rate_ip = False, prefix = "rate_",
|
||||
seconds = None):
|
||||
@@ -736,16 +747,13 @@ class VCacheKey(Validator):
|
||||
self.key = key
|
||||
if key:
|
||||
uid = g.cache.get(str(self.cache_prefix + "_" + self.key))
|
||||
try:
|
||||
a = Account._byID(uid, data = True)
|
||||
if name and a.name.lower() != name.lower():
|
||||
self.set_error(errors.BAD_USERNAME)
|
||||
else:
|
||||
self.user = a
|
||||
except NotFound:
|
||||
self.set_error(errors.BAD_USERNAME)
|
||||
if uid:
|
||||
try:
|
||||
self.user = Account._byID(uid, data = True)
|
||||
except NotFound:
|
||||
return
|
||||
#found everything we need
|
||||
return self
|
||||
|
||||
self.set_error(errors.EXPIRED)
|
||||
|
||||
class VOneOf(Validator):
|
||||
|
||||
@@ -51,6 +51,7 @@ def simple_email(to, fr, subj, body):
|
||||
def password_email(user):
|
||||
key = passhash(random.randint(0, 1000), user.email)
|
||||
passlink = 'http://' + g.domain + '/resetpassword/' + key
|
||||
print passlink
|
||||
cache.set("reset_%s" %key, user._id, time=1800)
|
||||
simple_email(user.email, 'reddit@reddit.com',
|
||||
'reddit.com password reset',
|
||||
|
||||
@@ -88,13 +88,19 @@ def unsafe(text=''):
|
||||
def websafe_json(text=""):
|
||||
return c_websafe_json(_force_unicode(text))
|
||||
|
||||
def websafe(text=''):
|
||||
def mako_websafe(text = ''):
|
||||
if text.__class__ == _Unsafe:
|
||||
return text
|
||||
elif text.__class__ != unicode:
|
||||
text = _force_unicode(text)
|
||||
return c_websafe(text)
|
||||
|
||||
def websafe(text=''):
|
||||
if text.__class__ != unicode:
|
||||
text = _force_unicode(text)
|
||||
#wrap the response in _Unsafe so make_websafe doesn't unescape it
|
||||
return _Unsafe(c_websafe(text))
|
||||
|
||||
from mako.filters import url_escape
|
||||
def edit_comment_filter(text = ''):
|
||||
try:
|
||||
|
||||
@@ -26,11 +26,17 @@ 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.models import IDBuilder, Listing
|
||||
|
||||
import simplejson
|
||||
from pylons import c
|
||||
from pylons import c, g
|
||||
|
||||
def json_respond(x):
|
||||
return websafe_json(simplejson.dumps(x or ''))
|
||||
if g.debug:
|
||||
return websafe_json(simplejson.dumps(x or '',
|
||||
sort_keys=True, indent=4))
|
||||
else:
|
||||
return websafe_json(simplejson.dumps(x or ''))
|
||||
|
||||
class JsonResponse(object):
|
||||
"""
|
||||
@@ -42,14 +48,14 @@ class JsonResponse(object):
|
||||
self._clear()
|
||||
|
||||
def _clear(self):
|
||||
self._has_errors = set([])
|
||||
self._errors = set()
|
||||
self._new_captcha = False
|
||||
self._data = {}
|
||||
|
||||
def send_failure(self, error):
|
||||
c.errors.add(error)
|
||||
self._clear()
|
||||
self._has_errors.add(error)
|
||||
self._errors.add((error, None))
|
||||
|
||||
def __call__(self, *a):
|
||||
return self
|
||||
@@ -57,58 +63,39 @@ class JsonResponse(object):
|
||||
def __getattr__(self, key):
|
||||
return self
|
||||
|
||||
def __iter__(self):
|
||||
def make_response(self):
|
||||
res = {}
|
||||
if self._data:
|
||||
res['data'] = self._data
|
||||
res['errors'] = [(e, c.errors[e].message) for e in self._has_errors]
|
||||
yield ("json", res)
|
||||
|
||||
def _mark_error(self, e):
|
||||
pass
|
||||
|
||||
def _unmark_error(self, e):
|
||||
pass
|
||||
res['errors'] = [(e[0], c.errors[e].message) for e in self._errors]
|
||||
return {"json": res}
|
||||
|
||||
def set_error(self, error_name, field_name):
|
||||
self._errors.add((error_name, field_name))
|
||||
|
||||
def has_error(self):
|
||||
return bool(self._has_errors)
|
||||
return bool(self._errors)
|
||||
|
||||
def has_errors(self, input, *errors, **kw):
|
||||
rval = False
|
||||
for e in errors:
|
||||
if e in c.errors:
|
||||
# get list of params checked to generate this error
|
||||
# if they exist, make sure they match input checked
|
||||
fields = c.errors[e].fields
|
||||
if not input or not fields or input in fields:
|
||||
self._has_errors.add(e)
|
||||
rval = True
|
||||
self._mark_error(e)
|
||||
else:
|
||||
self._unmark_error(e)
|
||||
|
||||
if rval and input:
|
||||
self.focus_input(input)
|
||||
return rval
|
||||
|
||||
def clear_errors(self, *errors):
|
||||
for e in errors:
|
||||
if e in self._has_errors:
|
||||
self._has_errors.remove(e)
|
||||
self._unmark_error(e)
|
||||
def has_errors(self, field_name, *errors, **kw):
|
||||
have_error = False
|
||||
for error_name in errors:
|
||||
if (error_name, field_name) in c.errors:
|
||||
self.set_error(error_name, field_name)
|
||||
have_error = True
|
||||
return have_error
|
||||
|
||||
def _things(self, things, action, *a, **kw):
|
||||
"""
|
||||
function for inserting/replacing things in listings.
|
||||
"""
|
||||
from r2.models import IDBuilder, Listing
|
||||
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):
|
||||
b = IDBuilder([t._fullname 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]
|
||||
|
||||
@@ -162,7 +149,9 @@ class JQueryResponse(JsonResponse):
|
||||
JsonResponse._clear(self)
|
||||
|
||||
def send_failure(self, error):
|
||||
JsonResponse.send_failure(self, error)
|
||||
c.errors.add(error)
|
||||
self._clear()
|
||||
self._errors.add((self, error, None))
|
||||
self.refresh()
|
||||
|
||||
def __call__(self, *a):
|
||||
@@ -178,8 +167,25 @@ class JQueryResponse(JsonResponse):
|
||||
self.ops.append([self.objs[obj], newi, op, args])
|
||||
return new
|
||||
|
||||
def __iter__(self):
|
||||
yield ("jquery", self.ops)
|
||||
def set_error(self, error_name, field_name):
|
||||
#self is the form that had the error checked, but we need to
|
||||
#add this error to the top_node of this response and give it a
|
||||
#reference to the form.
|
||||
self.top_node._errors.add((self, error_name, field_name))
|
||||
|
||||
def has_error(self):
|
||||
return bool(self.top_node._errors)
|
||||
|
||||
def make_response(self):
|
||||
#add the error messages
|
||||
for (form, error_name, field_name) in self._errors:
|
||||
selector = ".error." + error_name
|
||||
if field_name:
|
||||
selector += ".field-" + field_name
|
||||
message = c.errors[(error_name, field_name)].message
|
||||
form.find(selector).show().html(message).end()
|
||||
|
||||
return {"jquery": self.ops}
|
||||
|
||||
# thing methods
|
||||
#--------------
|
||||
@@ -196,22 +202,17 @@ class JQueryResponse(JsonResponse):
|
||||
|
||||
# convenience methods:
|
||||
# --------------------
|
||||
def _mark_error(self, e):
|
||||
self.find("." + e).show().html(c.errors[e].message).end()
|
||||
|
||||
def _unmark_error(self, e):
|
||||
self.find("." + e).html("").end()
|
||||
#def _mark_error(self, e, field):
|
||||
# self.find("." + e).show().html(c.errors[e].message).end()
|
||||
#
|
||||
#def _unmark_error(self, e):
|
||||
# self.find("." + e).html("").end()
|
||||
|
||||
def new_captcha(self):
|
||||
if not self._new_captcha:
|
||||
self.captcha(get_iden())
|
||||
self._new_captcha = True
|
||||
|
||||
def chk_captcha(self, *errors):
|
||||
if self.has_errors(None, *errors):
|
||||
self.new_captcha()
|
||||
return True
|
||||
|
||||
def get_input(self, name):
|
||||
return self.find("*[name=%s]" % name)
|
||||
|
||||
|
||||
@@ -42,10 +42,6 @@ def make_typename(typ):
|
||||
def make_fullname(typ, _id):
|
||||
return '%s_%s' % (make_typename(typ), to36(_id))
|
||||
|
||||
def mass_part_render(thing, **kw):
|
||||
return dict([(k, spaceCompress(thing.part_render(v)).strip(' ')) \
|
||||
for k, v in kw.iteritems()])
|
||||
|
||||
class JsonTemplate(Template):
|
||||
def __init__(self): pass
|
||||
|
||||
@@ -253,8 +249,8 @@ class CommentJsonTemplate(ThingJsonTemplate):
|
||||
else:
|
||||
parent_id = make_fullname(Comment, parent_id)
|
||||
d = ThingJsonTemplate.rendered_data(self, wrapped)
|
||||
d.update(mass_part_render(wrapped, contentHTML = 'commentBody',
|
||||
contentTxt = 'commentText'))
|
||||
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)
|
||||
return d
|
||||
@@ -268,6 +264,9 @@ class MoreCommentJsonTemplate(CommentJsonTemplate):
|
||||
def kind(self, wrapped):
|
||||
return "more"
|
||||
|
||||
def rendered_data(self, wrapped):
|
||||
return ThingJsonTemplate.rendered_data(self, wrapped)
|
||||
|
||||
class MessageJsonTemplate(ThingJsonTemplate):
|
||||
_data_attrs_ = ThingJsonTemplate.data_attrs(new = "new",
|
||||
subject = "subject",
|
||||
|
||||
@@ -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
|
||||
from wrapped import Wrapped, Styled
|
||||
from pylons import c, request, g
|
||||
from utils import query_string, timeago
|
||||
from strings import StringHandler, plurals
|
||||
@@ -150,30 +150,6 @@ menu = MenuHandler(hot = _('hot'),
|
||||
current_promos = _('promoted links'),
|
||||
)
|
||||
|
||||
class Styled(Wrapped):
|
||||
"""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
|
||||
name matches 'style'.
|
||||
|
||||
Additionally, when rendering, the '_id' and 'css_class' attributes
|
||||
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)
|
||||
|
||||
def render(self, **kw):
|
||||
"""Using the canonical template file, only renders the <%def>
|
||||
in the template whose name is given by self.style"""
|
||||
style = kw.get('style', c.render_style or 'html')
|
||||
return Wrapped.part_render(self, self.style, style = style, **kw)
|
||||
|
||||
|
||||
|
||||
def menu_style(type):
|
||||
"""Simple manager function for the styled menus. Returns a
|
||||
(style, css_class) pair given a 'type', defaulting to style =
|
||||
@@ -185,12 +161,11 @@ def menu_style(type):
|
||||
srdrop = ('dropdown', 'srdrop'),
|
||||
flatlist = ('flatlist', 'flat-list'),
|
||||
tabmenu = ('tabmenu', ''),
|
||||
formtab = ('tabmenu', 'formtab'),
|
||||
flat_vert = ('flatlist', 'flat-vert'),
|
||||
)
|
||||
return d.get(type, default)
|
||||
|
||||
|
||||
|
||||
class NavMenu(Styled):
|
||||
"""generates a navigation menu. The intention here is that the
|
||||
'style' parameter sets what template/layout to use to differentiate, say,
|
||||
@@ -337,7 +312,7 @@ class JsButton(NavButton):
|
||||
"""A button which fires a JS event and thus has no path and cannot
|
||||
be in the 'selected' state"""
|
||||
def __init__(self, title, style = 'js', **kw):
|
||||
NavButton.__init__(self, title, '', style = style, **kw)
|
||||
NavButton.__init__(self, title, '#', style = style, **kw)
|
||||
|
||||
def build(self, *a, **kw):
|
||||
self.path = 'javascript:void(0)'
|
||||
@@ -391,7 +366,7 @@ class SortMenu(SimpleGetMenu):
|
||||
options = ('hot', 'new', 'top', 'old', 'controversial')
|
||||
|
||||
def __init__(self, **kw):
|
||||
kw['title'] = _("sort by")
|
||||
kw['title'] = _("sorted by")
|
||||
SimpleGetMenu.__init__(self, **kw)
|
||||
|
||||
@classmethod
|
||||
@@ -521,6 +496,11 @@ class SubredditMenu(NavMenu):
|
||||
"""Always return False so the title is always displayed"""
|
||||
return None
|
||||
|
||||
class JsNavMenu(NavMenu):
|
||||
def find_selected(self):
|
||||
"""Always return the first element."""
|
||||
return self.options[0]
|
||||
|
||||
# --------------------
|
||||
# TODO: move to admin area
|
||||
class AdminReporterMenu(SortMenu):
|
||||
|
||||
@@ -19,8 +19,11 @@
|
||||
# All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
# CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
from r2.lib.wrapped import Wrapped, NoTemplateFound
|
||||
from r2.models import IDBuilder, LinkListing, Account, Default, FakeSubreddit, Subreddit, Friends, All, Sub, NotFound, DomainSR
|
||||
from r2.lib.wrapped import Wrapped, NoTemplateFound, Styled
|
||||
from r2.models import IDBuilder, LinkListing, Account, Default
|
||||
from r2.models import FakeSubreddit, Subreddit
|
||||
from r2.models import Friends, All, Sub, NotFound, DomainSR
|
||||
from r2.models import make_wrapper
|
||||
from r2.config import cache
|
||||
from r2.lib.jsonresponse import json_respond
|
||||
from r2.lib.jsontemplates import is_api
|
||||
@@ -32,13 +35,16 @@ from r2.lib.traffic import load_traffic, load_summary
|
||||
from r2.lib.captcha import get_iden
|
||||
from r2.lib.filters import spaceCompress, _force_unicode, _force_utf8
|
||||
from r2.lib.menus import NavButton, NamedButton, NavMenu, PageNameNav, JsButton
|
||||
from r2.lib.menus import SubredditButton, SubredditMenu, OffsiteButton, menu
|
||||
from r2.lib.menus import SubredditButton, SubredditMenu
|
||||
from r2.lib.menus import OffsiteButton, menu, JsNavMenu
|
||||
from r2.lib.strings import plurals, rand_strings, strings, Score
|
||||
from r2.lib.utils import title_to_url, query_string, UrlParser, to_js, vote_hash
|
||||
from r2.lib.template_helpers import add_sr, get_domain
|
||||
from r2.lib.subreddit_search import popular_searches
|
||||
|
||||
import sys, random, datetime, locale, calendar, re
|
||||
import sys, random, datetime, locale, calendar, simplejson, re
|
||||
import graph
|
||||
from itertools import chain
|
||||
from urllib import quote
|
||||
|
||||
datefmt = _force_utf8(_('%d %b %Y'))
|
||||
@@ -274,7 +280,6 @@ class Reddit(Wrapped):
|
||||
|
||||
if c.user_is_loggedin:
|
||||
more_buttons.append(NamedButton('saved', False))
|
||||
more_buttons.append(NamedButton('recommended', False))
|
||||
|
||||
if c.user_is_admin:
|
||||
more_buttons.append(NamedButton('admin'))
|
||||
@@ -287,6 +292,11 @@ class Reddit(Wrapped):
|
||||
if c.user_is_admin:
|
||||
more_buttons.append(NamedButton('traffic'))
|
||||
|
||||
#if there's only one button in the dropdown, get rid of the dropdown
|
||||
if len(more_buttons) == 1:
|
||||
main_buttons.append(more_buttons[0])
|
||||
more_buttons = []
|
||||
|
||||
toolbar = [NavMenu(main_buttons, type='tabmenu')]
|
||||
if more_buttons:
|
||||
toolbar.append(NavMenu(more_buttons, title=menu.more, type='tabdrop'))
|
||||
@@ -300,13 +310,13 @@ class Reddit(Wrapped):
|
||||
return "<Reddit>"
|
||||
|
||||
@staticmethod
|
||||
def content_stack(*a):
|
||||
def content_stack(panes, css_class = None):
|
||||
"""Helper method for reordering the content stack."""
|
||||
return PaneStack(filter(None, a))
|
||||
return PaneStack(filter(None, panes), css_class = css_class)
|
||||
|
||||
def content(self):
|
||||
"""returns a Wrapped (or renderable) item for the main content div."""
|
||||
return self.content_stack(self.infobar, self.nav_menu, self._content)
|
||||
return self.content_stack((self.infobar, self.nav_menu, self._content))
|
||||
|
||||
class ClickGadget(Wrapped):
|
||||
def __init__(self, links, *a, **kw):
|
||||
@@ -417,11 +427,15 @@ class MessagePage(Reddit):
|
||||
if not kw.has_key('show_sidebar'):
|
||||
kw['show_sidebar'] = False
|
||||
Reddit.__init__(self, *a, **kw)
|
||||
self.replybox = CommentReplyBox()
|
||||
self.replybox = UserText(item = None, creating = True,
|
||||
post_form = 'comment', display = False,
|
||||
cloneable = True)
|
||||
|
||||
def content(self):
|
||||
return self.content_stack(self.replybox, self.infobar,
|
||||
self.nav_menu, self._content)
|
||||
return self.content_stack((self.replybox,
|
||||
self.infobar,
|
||||
self.nav_menu,
|
||||
self._content))
|
||||
|
||||
def build_toolbars(self):
|
||||
buttons = [NamedButton('compose'),
|
||||
@@ -486,8 +500,11 @@ class LoginPage(BoringPage):
|
||||
class Login(Wrapped):
|
||||
"""The two-unit login and register form."""
|
||||
def __init__(self, user_reg = '', user_login = '', dest=''):
|
||||
Wrapped.__init__(self, user_reg = user_reg, user_login = user_login,
|
||||
dest = dest)
|
||||
Wrapped.__init__(self,
|
||||
user_reg = user_reg,
|
||||
user_login = user_login,
|
||||
dest = dest,
|
||||
captcha = Captcha())
|
||||
|
||||
|
||||
class SearchPage(BoringPage):
|
||||
@@ -501,8 +518,8 @@ class SearchPage(BoringPage):
|
||||
BoringPage.__init__(self, pagename, robots='noindex', *a, **kw)
|
||||
|
||||
def content(self):
|
||||
return self.content_stack(self.searchbar, self.infobar,
|
||||
self.nav_menu, self._content)
|
||||
return self.content_stack((self.searchbar, self.infobar,
|
||||
self.nav_menu, self._content))
|
||||
|
||||
class CommentsPanel(Wrapped):
|
||||
"""the side-panel on the reddit toolbar frame that shows the top
|
||||
@@ -530,10 +547,11 @@ class LinkInfoPage(Reddit):
|
||||
|
||||
def __init__(self, link = None, comment = None,
|
||||
link_title = '', *a, **kw):
|
||||
# TODO: temp hack until we find place for builder_wrapper
|
||||
from r2.controllers.listingcontroller import ListingController
|
||||
wrapper = make_wrapper(ListingController.builder_wrapper,
|
||||
expand_children = True)
|
||||
link_builder = IDBuilder(link._fullname,
|
||||
wrap = ListingController.builder_wrapper)
|
||||
wrap = wrapper)
|
||||
|
||||
# link_listing will be the one-element listing at the top
|
||||
self.link_listing = LinkListing(link_builder, nextprev=False).listing()
|
||||
@@ -553,6 +571,7 @@ class LinkInfoPage(Reddit):
|
||||
else:
|
||||
params = {'title':_force_unicode(link_title), 'site' : c.site.name}
|
||||
title = strings.link_info_title % params
|
||||
|
||||
Reddit.__init__(self, title = title, *a, **kw)
|
||||
|
||||
def build_toolbars(self):
|
||||
@@ -579,8 +598,11 @@ class LinkInfoPage(Reddit):
|
||||
return toolbar
|
||||
|
||||
def content(self):
|
||||
return self.content_stack(self.infobar, self.link_listing,
|
||||
self.nav_menu, self._content)
|
||||
return self.content_stack((self.infobar, self.link_listing,
|
||||
PaneStack([PaneStack((self.nav_menu,
|
||||
self._content))],
|
||||
title = _("comments"),
|
||||
css_class = "commentarea")))
|
||||
|
||||
def rightbox(self):
|
||||
rb = Reddit.rightbox(self)
|
||||
@@ -612,8 +634,6 @@ class EditReddit(Reddit):
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
|
||||
class SubredditsPage(Reddit):
|
||||
"""container for rendering a list of reddits. The corner
|
||||
searchbox is hidden and its functionality subsumed by an in page
|
||||
@@ -651,8 +671,8 @@ class SubredditsPage(Reddit):
|
||||
NavMenu(buttons, base_path = '/reddits', type="tabmenu")]
|
||||
|
||||
def content(self):
|
||||
return self.content_stack(self.searchbar, self.nav_menu,
|
||||
self.sr_infobar, self._content)
|
||||
return self.content_stack((self.searchbar, self.nav_menu,
|
||||
self.sr_infobar, self._content))
|
||||
|
||||
def rightbox(self):
|
||||
ps = Reddit.rightbox(self)
|
||||
@@ -663,7 +683,7 @@ class MySubredditsPage(SubredditsPage):
|
||||
"""Same functionality as SubredditsPage, without the search box."""
|
||||
|
||||
def content(self):
|
||||
return self.content_stack(self.nav_menu, self.infobar, self._content)
|
||||
return self.content_stack((self.nav_menu, self.infobar, self._content))
|
||||
|
||||
|
||||
def votes_visible(user):
|
||||
@@ -868,15 +888,6 @@ class Captcha(Wrapped):
|
||||
self.iden = get_captcha()
|
||||
Wrapped.__init__(self)
|
||||
|
||||
class CommentReplyBox(Wrapped):
|
||||
"""Used on LinkInfoPage to render the comment reply form at the
|
||||
top of the comment listing as well as the template for the forms
|
||||
which are JS inserted when clicking on 'reply' in either a comment
|
||||
or message listing."""
|
||||
def __init__(self, link_name='', captcha=None, action = 'comment'):
|
||||
Wrapped.__init__(self, link_name = link_name, captcha = captcha,
|
||||
action = action)
|
||||
|
||||
class PermalinkMessage(Wrapped):
|
||||
"""renders the box on comment pages that state 'you are viewing a
|
||||
single comment's thread'"""
|
||||
@@ -886,12 +897,14 @@ class PermalinkMessage(Wrapped):
|
||||
class PaneStack(Wrapped):
|
||||
"""Utility class for storing and rendering a list of block elements."""
|
||||
|
||||
def __init__(self, panes=[], div_id = None, css_class=None, div=False):
|
||||
def __init__(self, panes=[], div_id = None, css_class=None, div=False,
|
||||
title=""):
|
||||
div = div or div_id or css_class or False
|
||||
self.div_id = div_id
|
||||
self.css_class = css_class
|
||||
self.div = div
|
||||
self.stack = list(panes)
|
||||
self.title = title
|
||||
Wrapped.__init__(self)
|
||||
|
||||
def append(self, item):
|
||||
@@ -952,7 +965,6 @@ class Frame(Wrapped):
|
||||
dorks_re = re.compile(r"https?://?([-\w.]*\.)?digg\.com/\w+\.\w+(/|$)")
|
||||
class FrameToolbar(Wrapped):
|
||||
"""The reddit voting toolbar used together with Frame."""
|
||||
extension_handling = False
|
||||
def __init__(self, link = None, title = None, url = None, expanded = False, **kw):
|
||||
self.title = title
|
||||
self.url = url
|
||||
@@ -998,11 +1010,41 @@ class FrameToolbar(Wrapped):
|
||||
|
||||
Wrapped.__init__(self, **kw)
|
||||
|
||||
extension_handling = False
|
||||
|
||||
class NewLink(Wrapped):
|
||||
"""Render the link submission form"""
|
||||
def __init__(self, captcha = None, url = '', title= '', subreddits = (),
|
||||
then = 'comments'):
|
||||
tabs = (('link', ('link-desc', 'url-field')),
|
||||
('text', ('text-desc', 'text-field')))
|
||||
all_fields = set(chain(*(parts for (tab, parts) in tabs)))
|
||||
|
||||
buttons = []
|
||||
self.default_tabs = tabs[0][1]
|
||||
self.default_tab = tabs[0][0]
|
||||
for tab_name, parts in tabs:
|
||||
to_show = ','.join('#' + p for p in parts)
|
||||
to_hide = ','.join('#' + p for p in all_fields if p not in parts)
|
||||
onclick = "return select_form_tab(this, '%s', '%s');"
|
||||
onclick = onclick % (to_show, to_hide)
|
||||
|
||||
if tab_name == self.default_tab:
|
||||
self.default_show = to_show
|
||||
self.default_hide = to_hide
|
||||
|
||||
buttons.append(JsButton(tab_name, onclick=onclick, css_class=tab_name))
|
||||
|
||||
self.formtabs_menu = JsNavMenu(buttons, type = 'formtab')
|
||||
self.default_tabs = tabs[0][1]
|
||||
|
||||
self.sr_searches = simplejson.dumps(popular_searches())
|
||||
|
||||
if isinstance(c.site, FakeSubreddit):
|
||||
self.default_sr = subreddits[0] if subreddits else g.default_sr
|
||||
else:
|
||||
self.default_sr = c.site.name
|
||||
|
||||
Wrapped.__init__(self, captcha = captcha, url = url,
|
||||
title = title, subreddits = subreddits,
|
||||
then = then)
|
||||
@@ -1082,11 +1124,22 @@ class ButtonDemoPanel(Wrapped):
|
||||
|
||||
class Feedback(Wrapped):
|
||||
"""The feedback and ad inquery form(s)"""
|
||||
def __init__(self, captcha=None, title=None, action='/feedback',
|
||||
message='', name='', email='', replyto='', success = False):
|
||||
Wrapped.__init__(self, captcha = captcha, title = title, action = action,
|
||||
message = message, name = name, email = email, replyto = replyto,
|
||||
success = success)
|
||||
def __init__(self, title, action):
|
||||
email = name = ''
|
||||
if c.user_is_loggedin:
|
||||
email = getattr(c.user, "email", "")
|
||||
name = c.user.name
|
||||
|
||||
captcha = None
|
||||
if not c.user_is_loggedin or c.user.needs_captcha():
|
||||
captcha = Captcha()
|
||||
|
||||
Wrapped.__init__(self,
|
||||
captcha = captcha,
|
||||
title = title,
|
||||
action = action,
|
||||
email = email,
|
||||
name = name)
|
||||
|
||||
|
||||
class WidgetDemoPanel(Wrapped):
|
||||
@@ -1260,7 +1313,7 @@ class DetailsPage(LinkInfoPage):
|
||||
def content(self):
|
||||
# TODO: a better way?
|
||||
from admin_pages import Details
|
||||
return self.content_stack(self.link_listing, Details(link = self.link))
|
||||
return self.content_stack((self.link_listing, Details(link = self.link)))
|
||||
|
||||
class Cnameframe(Wrapped):
|
||||
"""The frame page."""
|
||||
@@ -1289,8 +1342,7 @@ class PromotePage(Reddit):
|
||||
buttons = [NamedButton('current_promos', dest = ''),
|
||||
NamedButton('new_promo')]
|
||||
|
||||
menu = NavMenu(buttons, title='show', base_path = '/promote',
|
||||
type='flatlist')
|
||||
menu = NavMenu(buttons, base_path = '/promote', type='flatlist')
|
||||
|
||||
if nav_menus:
|
||||
nav_menus.insert(0, menu)
|
||||
@@ -1330,6 +1382,83 @@ class PromoteLinkForm(Wrapped):
|
||||
listing = listing,
|
||||
*a, **kw)
|
||||
|
||||
class TabbedPane(Wrapped):
|
||||
def __init__(self, tabs):
|
||||
"""Renders as tabbed area where you can choose which tab to
|
||||
render. Tabs is a list of tuples (tab_name, tab_pane)."""
|
||||
buttons = []
|
||||
for tab_name, title, pane in tabs:
|
||||
buttons.append(JsButton(title, onclick="return select_tab_menu(this, '%s');" % tab_name))
|
||||
|
||||
self.tabmenu = JsNavMenu(buttons, type = 'tabpane')
|
||||
self.tabs = tabs
|
||||
|
||||
Wrapped.__init__(self)
|
||||
|
||||
class LinkChild(Wrapped):
|
||||
def __init__(self, link, load = False, expand = False, nofollow = False):
|
||||
self.link = link
|
||||
self.expand = expand
|
||||
self.load = load or expand
|
||||
self.nofollow = nofollow
|
||||
Wrapped.__init__(self)
|
||||
|
||||
def content(self):
|
||||
return ''
|
||||
|
||||
class MediaChild(LinkChild):
|
||||
css_style = "video"
|
||||
def content(self):
|
||||
return self.link.media_object
|
||||
|
||||
class SelfTextChild(LinkChild):
|
||||
css_style = "selftext"
|
||||
def content(self):
|
||||
u = UserText(self.link, self.link.selftext,
|
||||
editable = c.user == self.link.author,
|
||||
nofollow = self.nofollow)
|
||||
#have to force the render style to html for now cause of some
|
||||
#c.render_style weirdness
|
||||
return u.render(style = 'html')
|
||||
|
||||
class SelfText(Wrapped):
|
||||
def __init__(self, link):
|
||||
Wrapped.__init__(self, link = link)
|
||||
|
||||
class UserText(Wrapped):
|
||||
def __init__(self,
|
||||
item,
|
||||
text = '',
|
||||
have_form = True,
|
||||
editable = False,
|
||||
creating = False,
|
||||
nofollow = False,
|
||||
display = True,
|
||||
post_form = 'editusertext',
|
||||
cloneable = False,
|
||||
extra_css = ''):
|
||||
|
||||
css_class = "usertext"
|
||||
if cloneable:
|
||||
css_class += " cloneable"
|
||||
if extra_css:
|
||||
css_class += " " + extra_css
|
||||
|
||||
Wrapped.__init__(self,
|
||||
item = item,
|
||||
text = text,
|
||||
have_form = have_form,
|
||||
editable = editable,
|
||||
creating = creating,
|
||||
nofollow = nofollow,
|
||||
display = display,
|
||||
post_form = post_form,
|
||||
cloneable = cloneable,
|
||||
css_class = css_class)
|
||||
|
||||
def button(self):
|
||||
pass
|
||||
|
||||
class Traffic(Wrapped):
|
||||
@staticmethod
|
||||
def slice_traffic(traffic, *indices):
|
||||
@@ -1491,3 +1620,4 @@ class RedditTraffic(Traffic):
|
||||
class InnerToolbarFrame(Wrapped):
|
||||
def __init__(self, link, expanded = False):
|
||||
Wrapped.__init__(self, link = link, expanded = expanded)
|
||||
|
||||
|
||||
@@ -281,7 +281,7 @@ def make_scraper(url):
|
||||
|
||||
#Youtube
|
||||
class YoutubeScraper(MediaScraper):
|
||||
media_template = '<object width="425" height="350"><param name="movie" value="http://www.youtube.com/v/$video_id"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/$video_id" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"></embed></object>'
|
||||
media_template = '<object width="480" height="295"><param name="movie" value="http://www.youtube-nocookie.com/v/$video_id"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube-nocookie.com/v/$video_id" type="application/x-shockwave-flash" wmode="transparent" width="480" height="295"></embed></object>'
|
||||
thumbnail_template = 'http://img.youtube.com/vi/$video_id/default.jpg'
|
||||
video_id_rx = re.compile('.*v=([A-Za-z0-9-_]+).*')
|
||||
|
||||
|
||||
@@ -121,6 +121,9 @@ string_dict = dict(
|
||||
also get there by clicking the link's title
|
||||
(in the middle of the toolbar, to the right of the comments button).
|
||||
"""),
|
||||
|
||||
submit_link = _("""You are submitting a link. The key to a successful submission is interesting content and a deceptive title."""),
|
||||
submit_text = _("""You are submitting a text-based post. Speak your mind. A title is required, but expanding further in the text field is not. Beginning your title with "vote up if" is violation of intergalactic law."""),
|
||||
)
|
||||
|
||||
class StringHandler(object):
|
||||
|
||||
46
r2/r2/lib/subreddit_search.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from r2.models import *
|
||||
from r2.lib import utils
|
||||
|
||||
from pylons import g
|
||||
|
||||
sr_prefix = 'sr_search_'
|
||||
|
||||
|
||||
def load_all_reddits():
|
||||
query_cache = {}
|
||||
|
||||
q = Subreddit._query(Subreddit.c.type == 'public',
|
||||
Subreddit.c._downs > 1,
|
||||
sort = (desc('_downs'), desc('_ups')),
|
||||
data = True)
|
||||
for sr in utils.fetch_things2(q):
|
||||
name = sr.name.lower()
|
||||
for i in xrange(len(name)):
|
||||
prefix = name[:i + 1]
|
||||
names = query_cache.setdefault(prefix, [])
|
||||
if len(names) < 10:
|
||||
names.append(sr.name)
|
||||
|
||||
g.rendercache.set_multi(query_cache, prefix = sr_prefix)
|
||||
|
||||
def search_reddits_cached(query):
|
||||
return g.rendercache.get(sr_prefix + query) or []
|
||||
|
||||
def search_reddits(query):
|
||||
return search_reddits_cached(str(query.lower()))
|
||||
|
||||
@memoize('popular_searches', time = 3600)
|
||||
def popular_searches():
|
||||
top_reddits = Subreddit._query(Subreddit.c.type == 'public',
|
||||
sort = desc('_downs'),
|
||||
limit = 100,
|
||||
data = True)
|
||||
top_searches = {}
|
||||
for sr in top_reddits:
|
||||
name = sr.name.lower()
|
||||
for i in xrange(min(len(name), 3)):
|
||||
query = name[:i + 1]
|
||||
r = search_reddits(query)
|
||||
top_searches[query] = r
|
||||
return top_searches
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
# 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
|
||||
|
||||
@@ -102,8 +103,11 @@ def replace_render(listing, item, style = None, display = True):
|
||||
pass
|
||||
return rendered_item
|
||||
|
||||
child_txt = ( hasattr(item, "child") and item.child )\
|
||||
and item.child.render(style = style) or ""
|
||||
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):
|
||||
@@ -239,7 +243,7 @@ def add_sr(path, sr_path = True, nocname=False, force_hostname = False):
|
||||
path to include c.site.path.
|
||||
"""
|
||||
# don't do anything if it is just an anchor
|
||||
if path.startswith('#'):
|
||||
if path.startswith('#') or path.startswith('javascript:'):
|
||||
return path
|
||||
|
||||
u = UrlParser(path)
|
||||
|
||||
@@ -24,6 +24,7 @@ from utils import storage
|
||||
|
||||
from itertools import chain
|
||||
import sys
|
||||
|
||||
sys.setrecursionlimit(500)
|
||||
|
||||
class NoTemplateFound(Exception): pass
|
||||
@@ -111,3 +112,26 @@ def SimpleWrapped(**kw):
|
||||
kw.update(kw1)
|
||||
Wrapped.__init__(self, *a, **kw)
|
||||
return _SimpleWrapped
|
||||
|
||||
class Styled(Wrapped):
|
||||
"""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
|
||||
name matches 'style'.
|
||||
|
||||
Additionally, when rendering, the '_id' and 'css_class' attributes
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
@@ -596,6 +596,14 @@ class CommentBuilder(Builder):
|
||||
|
||||
return final
|
||||
|
||||
def make_wrapper(parent_wrapper = Wrapped, **params):
|
||||
def wrapper_fn(thing):
|
||||
w = parent_wrapper(thing)
|
||||
for k, v in params.iteritems():
|
||||
setattr(w, k, v)
|
||||
return w
|
||||
return wrapper_fn
|
||||
|
||||
class TopCommentBuilder(CommentBuilder):
|
||||
"""A comment builder to fetch only the top-level, non-spam,
|
||||
non-deleted comments"""
|
||||
|
||||
@@ -52,6 +52,7 @@ class Link(Thing, Printable):
|
||||
promote_until = None,
|
||||
promoted_by = None,
|
||||
disable_comments = False,
|
||||
selftext = '',
|
||||
ip = '0.0.0.0')
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
@@ -204,6 +205,7 @@ class Link(Thing, Printable):
|
||||
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),
|
||||
@@ -224,7 +226,12 @@ class Link(Thing, Printable):
|
||||
wrapped.show_reports,
|
||||
wrapped.can_ban,
|
||||
wrapped.thumbnail,
|
||||
wrapped.moderator_banned))
|
||||
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":
|
||||
@@ -284,7 +291,8 @@ class Link(Thing, Printable):
|
||||
item.thumbnail = thumbnail_url(item)
|
||||
else:
|
||||
item.thumbnail = g.default_thumb
|
||||
|
||||
|
||||
|
||||
item.score = max(0, item.score)
|
||||
|
||||
item.domain = (domain(item.url) if not item.is_self
|
||||
@@ -329,6 +337,20 @@ class Link(Thing, Printable):
|
||||
item.domain_path = "/domain/%s" % item.domain
|
||||
if item.is_self:
|
||||
item.domain_path = item.subreddit_path
|
||||
|
||||
#this is wrong, but won't be so wrong when we move this
|
||||
#whole chunk of code into pages.py
|
||||
from r2.lib.pages import MediaChild, SelfTextChild
|
||||
item.link_child = None
|
||||
item.editable = False
|
||||
if item.media_object:
|
||||
item.link_child = MediaChild(item, load = True)
|
||||
elif item.selftext:
|
||||
expand = getattr(item, 'expand_children', False)
|
||||
item.link_child = SelfTextChild(item, expand = expand,
|
||||
nofollow = item.nofollow)
|
||||
#draw the edit button if the contents are pre-expanded
|
||||
item.editable = expand and item.author == c.user
|
||||
|
||||
item.tblink = "http://%s/tb/%s" % (
|
||||
get_domain(cname = c.cname, subreddit=False),
|
||||
@@ -549,9 +571,18 @@ class Comment(Thing, Printable):
|
||||
item.author != c.user and
|
||||
not item.show_spam)))
|
||||
|
||||
if item._deleted and not c.user_is_admin:
|
||||
item.author = DeletedUser()
|
||||
item.body = '[deleted]'
|
||||
extra_css = ''
|
||||
if item._deleted:
|
||||
if c.user_is_admin:
|
||||
extra_css += "grayed"
|
||||
else:
|
||||
item.author = DeletedUser()
|
||||
item.body = '[deleted]'
|
||||
|
||||
|
||||
if c.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
|
||||
@@ -570,6 +601,12 @@ class Comment(Thing, Printable):
|
||||
item.score_fmt = Score.points
|
||||
item.permalink = item.make_permalink(item.link, item.subreddit)
|
||||
|
||||
#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,
|
||||
nofollow = item.nofollow,
|
||||
extra_css = extra_css)
|
||||
class StarkComment(Comment):
|
||||
"""Render class for the comments in the top-comments display in
|
||||
the reddit toolbar"""
|
||||
|
||||
BIN
r2/r2/public/static/blog-collapsed-hover.png
Normal file
|
After Width: | Height: | Size: 452 B |
BIN
r2/r2/public/static/blog-collapsed.png
Normal file
|
After Width: | Height: | Size: 428 B |
BIN
r2/r2/public/static/blog-expanded-hover.png
Normal file
|
After Width: | Height: | Size: 440 B |
BIN
r2/r2/public/static/blog-expanded.png
Normal file
|
After Width: | Height: | Size: 411 B |
@@ -23,6 +23,10 @@ body {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
textarea { font: normal small verdana, arial, helvetica, sans-serif; }
|
||||
|
||||
/*html,body { height: 100%; }*/
|
||||
|
||||
/* IE dumbness patch. hidden input in a hidden block that is
|
||||
* subsequently shown leads to the input to "show" and generate undesired
|
||||
* )padding. This makes it go away. */
|
||||
@@ -69,7 +73,6 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; }
|
||||
|
||||
/* forms */
|
||||
|
||||
.iform th { text-align: right; color: black; font-weight: normal; text-transform: lowercase; }
|
||||
.wrong {color: red; font-weight: normal}
|
||||
|
||||
.subform input.text { width: 25em }
|
||||
@@ -112,13 +115,15 @@ ul.flat-vert {text-align: left;}
|
||||
.pref { font-weight: bold; }
|
||||
|
||||
#header {
|
||||
|
||||
border-bottom: 1px solid #5f99cf;
|
||||
|
||||
position: relative;
|
||||
background-color: #cee3f8;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
#header-img {margin-top: 2px;}
|
||||
#header-img {margin-top: 2px; margin-right: 5px;}
|
||||
|
||||
#header-top {
|
||||
position: absolute;
|
||||
@@ -147,6 +152,7 @@ ul.flat-vert {text-align: left;}
|
||||
font-weight: bold;
|
||||
margin-right: 1ex;
|
||||
font-variant: small-caps;
|
||||
line-height: 100%;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
.pagename a {color: black; }
|
||||
@@ -234,19 +240,20 @@ ul.flat-vert {text-align: left;}
|
||||
white-space: nowrap;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.tabmenu li {
|
||||
display: inline;
|
||||
font-weight: bold;
|
||||
margin: 0px 3px;
|
||||
padding: 2px 6px 0px 6px;
|
||||
background-color: #eff7ff;
|
||||
}
|
||||
|
||||
.tabmenu li.selected { padding: 0; }
|
||||
.tabmenu li a {
|
||||
padding: 2px 6px 0 6px;
|
||||
background-color: #eff7ff;
|
||||
}
|
||||
|
||||
.tabmenu li.selected a{
|
||||
color: orangered;
|
||||
padding: 2px 6px 1px 6px;
|
||||
background-color: white;
|
||||
border: 1px solid #5f99cf;
|
||||
border-bottom: 1px solid white;
|
||||
@@ -458,7 +465,7 @@ before enabling */
|
||||
|
||||
.midcol {
|
||||
float: left;
|
||||
margin-right: 2px;
|
||||
margin-right: 4px;
|
||||
margin-left: 7px;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
@@ -609,6 +616,33 @@ before enabling */
|
||||
|
||||
.organic-help-button { padding: 0 .5ex; }
|
||||
|
||||
.menuarea {
|
||||
border-bottom: 1px dotted gray;
|
||||
padding: 5px 10px;
|
||||
margin: 5px 310px 5px 5px;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.menuarea .spacer {display: inline; margin-right: 15px}
|
||||
|
||||
.commentarea h1 {
|
||||
margin: 10px 310px 0px 10px;
|
||||
padding-bottom: 3px;
|
||||
border-bottom: 1px dotted gray;
|
||||
}
|
||||
|
||||
.commentarea .menuarea {
|
||||
border: none;
|
||||
margin: 0 310px 10px 10px;
|
||||
padding: 0;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.commentarea > .usertext {
|
||||
margin: 0 0 10px 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.infobar {
|
||||
background-color: #f6e69f;
|
||||
padding: 5px 10px;
|
||||
@@ -642,16 +676,6 @@ before enabling */
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.menuarea {
|
||||
border-bottom: 1px dotted gray;
|
||||
padding: 5px 10px;
|
||||
margin: 5px 310px 5px 5px;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.menuarea .spacer {display: inline; margin-right: 15px}
|
||||
|
||||
|
||||
/*top link*/
|
||||
a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
@@ -662,7 +686,8 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
.entry .buttons li {
|
||||
display: inline;
|
||||
padding: 0 4px;
|
||||
border: none;}
|
||||
border: none;
|
||||
}
|
||||
|
||||
.entry .buttons li.first {padding-left: 0px;}
|
||||
|
||||
@@ -753,6 +778,7 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
|
||||
.linkcompressed .midcol { width: 15px; }
|
||||
.linkcompressed .entry .buttons li a:hover { text-decoration: underline}
|
||||
.linkcompressed .expando-button {display: none}
|
||||
|
||||
|
||||
.warm-entry .rank { color: #EDA179; }
|
||||
@@ -822,57 +848,8 @@ a.star { text-decoration: none; color: #ff8b60 }
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.commentreply {
|
||||
margin: 10px;
|
||||
margin-left: 15px;
|
||||
width: 40em;
|
||||
}
|
||||
|
||||
textarea.gray { color: gray; }
|
||||
|
||||
.commentreply textarea {
|
||||
border: 1px solid #369;
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
.commentreply .buttons {
|
||||
margin: 0px;
|
||||
float: left;
|
||||
}
|
||||
.commentreply .buttons button {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.commentreply table.help {
|
||||
margin: 0px;
|
||||
margin-top: 5px;
|
||||
font-size: larger;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.commentreply .help,
|
||||
.commentreply .help td,
|
||||
.commentreply .help tr {
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
}
|
||||
.commentreply .help-toggle {
|
||||
float:right;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.permamessage {
|
||||
font-size: larger;
|
||||
border: 1px dotted black;
|
||||
padding: 5px 5px 5px 18px;
|
||||
white-space: nowrap;
|
||||
background-image: url(/static/permalink-arrow.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 5px center;
|
||||
}
|
||||
|
||||
.deepthread {
|
||||
padding-right: 30px;
|
||||
background-image: url(/static/continue-thread.png);
|
||||
@@ -916,8 +893,6 @@ textarea.gray { color: gray; }
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.commentbody { }
|
||||
|
||||
.commentbody.border { background-color: #ffc; padding-left: 5px}
|
||||
.commentbody.grayed {
|
||||
color: gray;
|
||||
@@ -1108,7 +1083,7 @@ textarea.gray { color: gray; }
|
||||
}
|
||||
|
||||
|
||||
.status { color: red; }
|
||||
.status { margin-left: 5px; color: red; font-size: small;}
|
||||
.error { color: red; font-size: small; margin: 5px; }
|
||||
.line-through { text-decoration: line-through }
|
||||
|
||||
@@ -1220,6 +1195,9 @@ textarea.gray { color: gray; }
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
#passform h1 {margin-bottom: 0px}
|
||||
#passform p {margin-bottom: 5px; font-size: small}
|
||||
|
||||
/* cover */
|
||||
.cover {
|
||||
position: fixed;
|
||||
@@ -1632,6 +1610,16 @@ textarea.gray { color: gray; }
|
||||
}
|
||||
|
||||
/* default form styles */
|
||||
|
||||
|
||||
form .spacer + .spacer {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
form input[type=checkbox],
|
||||
form input[type=radio] {margin: 2px .5em 0 0; }
|
||||
|
||||
|
||||
.pretty-form {
|
||||
font-size: larger;
|
||||
vertical-align: top;
|
||||
@@ -1639,7 +1627,7 @@ textarea.gray { color: gray; }
|
||||
|
||||
.pretty-form p {margin: 3px ;}
|
||||
.pretty-form input[type=checkbox],
|
||||
.pretty-form input[type=radio] {margin: 2px .5em 0px .5em; }
|
||||
.pretty-form input[type=radio] {margin: 2px .5em 0 0; }
|
||||
.pretty-form img { margin: 3px .5em}
|
||||
.pretty-form input[type=text],
|
||||
.pretty-form textarea,
|
||||
@@ -1660,29 +1648,24 @@ textarea.gray { color: gray; }
|
||||
.pretty-form select,
|
||||
.pretty-form b,
|
||||
.pretty-form textarea,
|
||||
.pretty-form button { margin: 3px .5em; }
|
||||
.pretty-form button { margin: 3px 5px; }
|
||||
.pretty-form th { text-align: right }
|
||||
|
||||
/* delete page */
|
||||
.delete-field {
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/*pref page boxes*/
|
||||
.pretty-form.short-text input[type=text],
|
||||
.pretty-form.short-text textarea,
|
||||
.pretty-form.short-text input[type=password] {width: 2em }
|
||||
|
||||
/*update password*/
|
||||
.pretty-form.medium-text input[type=text],
|
||||
.pretty-form.medium-text textarea,
|
||||
.pretty-form.medium-text input[type=password] {width: 15em }
|
||||
|
||||
/*submit*/
|
||||
.pretty-form.long-text input[type=text],
|
||||
.pretty-form.long-text textarea,
|
||||
.pretty-form.long-text input[type=password] {padding: 2px; width: 40em }
|
||||
|
||||
/*forgot password*/
|
||||
#passform h1 { margin: 0px; }
|
||||
#passform p { font-size: smaller; color: orangered; margin-bottom: 7px}
|
||||
#passform.pretty-form button { padding: 0px 1px; }
|
||||
|
||||
#url-field button {margin: 10px 5px 0 0;}
|
||||
#url-field .title-status { color: red; font-size: small}
|
||||
|
||||
/*opt-out/in form*/
|
||||
.opt-form { font-size: larger }
|
||||
@@ -1815,8 +1798,8 @@ ul#image-preview-list .description pre {
|
||||
|
||||
#preview-table > table > tbody > tr { padding-bottom: 10px; }
|
||||
#preview-table > table > tbody > tr > td { padding: 5px; padding-right: 15px;}
|
||||
#preview-table > table > tbody > tr > th { padding: 5px; padding-right: 15px;}
|
||||
#preview-table > table > tbody > tr > th {
|
||||
padding: 5px; padding-right: 15px;
|
||||
font-weight: bold;
|
||||
vertical-align: top;
|
||||
font-size: larger;
|
||||
@@ -1984,6 +1967,293 @@ ul#image-preview-list .description pre {
|
||||
|
||||
.toggle.deltranslator-button { display: inline; }
|
||||
|
||||
/****************/
|
||||
|
||||
#sr { margin-left: 0px }
|
||||
|
||||
#sr-list-wrapper {
|
||||
width: 454px;
|
||||
height: 200px;
|
||||
border: 1px solid gray;
|
||||
border-top: none;
|
||||
margin: 0 5px;
|
||||
font-size: smaller;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#sr-list-cover {
|
||||
position: absolute;
|
||||
background: gray url(/static/throbber.gif) no-repeat scroll center center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
opacity: .7;
|
||||
filter:alpha(opacity=70); /* IE patch */
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sr-list {
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
#sr-searchfield { margin: 0 5px; }
|
||||
|
||||
.sr-name {
|
||||
font-size: small;
|
||||
vertical-align: top;
|
||||
padding: 3px 3px 3px 0;
|
||||
}
|
||||
|
||||
.sr-description {
|
||||
padding: 3px
|
||||
}
|
||||
|
||||
.sr-row {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sr-row.sr-selected {
|
||||
background: #EFF7FF url(/static/rightarrow.png) no-repeat scroll 0px 5px;
|
||||
}
|
||||
|
||||
.sr-arrow {
|
||||
width: 10px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
#sr-autocomplete-area {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#sr-drop-down {
|
||||
position: absolute;
|
||||
width: 498px;
|
||||
border: 1px solid gray;
|
||||
background: white;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sr-drop-down table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.sr-name-row {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.sr-name-row.sr-selected {
|
||||
background-color: #369;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.submit-header {
|
||||
font-size: larger;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#suggested-reddits {
|
||||
margin-top: 5px;
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#suggested-reddits ul {
|
||||
|
||||
}
|
||||
|
||||
#suggested-reddits li {
|
||||
display: inline;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
|
||||
/*** new menu shit ***/
|
||||
|
||||
.formtabs-content {
|
||||
width: 520px;
|
||||
border-top: 4px solid #5f99cf;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.formtabs-content .infobar {
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
ul.tabmenu.formtab {
|
||||
display: block;
|
||||
padding-left: 10px;
|
||||
font-size: larger;
|
||||
}
|
||||
|
||||
.tabmenu.formtab li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tabmenu.formtab a {
|
||||
font-weight: normal;
|
||||
outline: none;
|
||||
padding: 0px 12px;
|
||||
vertical-align: bottom;
|
||||
|
||||
border: 1px solid #c1c1c1;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.tabmenu.formtab .selected a {
|
||||
color:white;
|
||||
font-size: 130%;
|
||||
background-color: #5f99cf;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
/******* embed stuff ******/
|
||||
.expando {
|
||||
clear: left;
|
||||
margin: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.expando-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.expando-button {
|
||||
float: left;
|
||||
height: 23px;
|
||||
width: 23px;
|
||||
margin: 2px 5px 2px 0;
|
||||
background: white none no-repeat scroll center center;
|
||||
}
|
||||
|
||||
.expando-button.selftext.collapsed {background-image: url(/static/blog-collapsed.png);}
|
||||
.expando-button.selftext.collapsed:hover, .eb-sch {background-image: url(/static/blog-collapsed-hover.png);}
|
||||
.expando-button.selftext.expanded, .eb-se {
|
||||
margin-bottom: 5px;
|
||||
background-image: url(/static/blog-expanded.png);
|
||||
}
|
||||
.expando-button.selftext.expanded:hover, .eb-seh {background-image: url(/static/blog-expanded-hover.png);}
|
||||
|
||||
.expando-button.video.collapsed {background-image: url(/static/vid-collapsed.png);}
|
||||
|
||||
.expando-button.video.collapsed:hover, .eb-vch {background-image: url(/static/vid-collapsed-hover.png);}
|
||||
.expando-button.video.expanded, .eb-ve {background-image: url(/static/vid-expanded.png);}
|
||||
.expando-button.video.expanded:hover, .eb-veh {background-image: url(/static/vid-expanded-hover.png);}
|
||||
|
||||
|
||||
/******** self text stuff ****/
|
||||
.link .usertext .md,
|
||||
.linkcompressed .usertext .md {
|
||||
padding: 0 5px;
|
||||
background-color: #fafafa;
|
||||
border: 1px solid #369;
|
||||
-moz-border-radius: 7px;
|
||||
-webkit-border-radius: 7px;
|
||||
}
|
||||
|
||||
.usertext {
|
||||
font-size: small;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.usertext-edit {
|
||||
margin-top: 5px;
|
||||
padding: 0 1px; /* so the border of help/textbox don't get chopped off */
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.usertext-edit textarea {
|
||||
width: 500px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/*permalinks*/
|
||||
.usertext.border .usertext-body {
|
||||
background-color: #ffc; padding-left: 5px;
|
||||
}
|
||||
|
||||
/*admin see deleted comment*/
|
||||
.usertext.grayed .usertext-body {
|
||||
color: gray;
|
||||
background-color: #e0e0e0;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.usertext button {
|
||||
margin: 5px 5px 10px 0;
|
||||
}
|
||||
|
||||
.usertext .help-toggle {
|
||||
font-size: smaller;
|
||||
float:right;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.usertext .bottom-area {
|
||||
/* this restricts children floats to the container */
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.usertext table.markhelp {
|
||||
background-color: white;
|
||||
margin: 5px 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.usertext .markhelp,
|
||||
.usertext .markhelp td,
|
||||
.usertext .markhelp tr {
|
||||
border: 1px solid #C0C0C0;
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.usertext .markhelp .spaces {background-color: #c0c0c0}
|
||||
|
||||
/*** roundfield stuff *******/
|
||||
.roundfield {
|
||||
width: 500px;
|
||||
background-color: #cee3f8;
|
||||
-moz-border-radius: 4px;
|
||||
-webkit-border-radius: 4px;
|
||||
padding: 5px 10px 10px 10px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.roundfield .title {
|
||||
}
|
||||
|
||||
.roundfield .roundfield-content {
|
||||
margin-top: 5px;
|
||||
border: none;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.roundfield .usertext-edit {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.roundfield textarea,
|
||||
.roundfield input[type=text],
|
||||
.roundfield input[type=password] {
|
||||
font-size: 100%;
|
||||
width: 492px;
|
||||
padding: 3px;
|
||||
margin: 0;
|
||||
border: 1px solid gray;
|
||||
}
|
||||
|
||||
.roundfield.captcha .capimage {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/***traffic stuff***/
|
||||
.traffic-table {margin: 10px 20px; }
|
||||
.traffic-table a:hover { text-decoration: underline; }
|
||||
.traffic-table th { font-weight: bold; text-align: center;}
|
||||
@@ -2005,3 +2275,6 @@ ul#image-preview-list .description pre {
|
||||
border: 1px solid #B0B0B0;
|
||||
margin-left: 10px;
|
||||
margin-bottom: 10px; }
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -125,8 +125,9 @@ function handleResponse(action) {
|
||||
objs[new_i] = objs[old_i][args];
|
||||
if(objs[new_i])
|
||||
objs[new_i]._obj = objs[old_i];
|
||||
else
|
||||
else {
|
||||
$.debug("unrecognized");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$.debug("unrecognized");
|
||||
@@ -137,7 +138,7 @@ function handleResponse(action) {
|
||||
};
|
||||
|
||||
var api_loc = '/api/';
|
||||
$.request = function(op, parameters, worker_in, block, get_only) {
|
||||
$.request = function(op, parameters, worker_in, block, type, get_only) {
|
||||
/*
|
||||
Uniquitous reddit AJAX poster. Automatically addes
|
||||
handleResponse(action) worker to deal with the API result. The
|
||||
@@ -153,6 +154,7 @@ $.request = function(op, parameters, worker_in, block, get_only) {
|
||||
|
||||
parameters = $.with_default(parameters, {});
|
||||
worker_in = $.with_default(worker_in, handleResponse(action));
|
||||
type = $.with_default(type, "json");
|
||||
if (typeof(worker_in) != 'function')
|
||||
worker_in = handleResponse(action);
|
||||
var worker = function(r) {
|
||||
@@ -178,10 +180,11 @@ $.request = function(op, parameters, worker_in, block, get_only) {
|
||||
op = api_loc + op;
|
||||
/*if( document.location.host == reddit.ajax_domain )
|
||||
/* normal AJAX post */
|
||||
|
||||
if(get_only) {
|
||||
$.get(op, parameters, worker, "json");
|
||||
$.get(op, parameters, worker, type);
|
||||
} else {
|
||||
$.post(op, parameters, worker, "json");
|
||||
$.post(op, parameters, worker, type);
|
||||
}
|
||||
/*else { /* cross domain it is... * /
|
||||
op = "http://" + reddit.ajax_domain + op + "?callback=?";
|
||||
@@ -359,7 +362,7 @@ $.fn.randomize_ids = function() {
|
||||
return $(this);
|
||||
}
|
||||
|
||||
$.replace_things = function(things, keep_children, reveal, stubs) {
|
||||
$.fn.replace_things = function(things, keep_children, reveal, stubs) {
|
||||
/* Given the api-html structured things, insert them into the DOM
|
||||
* in such a way as to remove any elements with the same thing_id.
|
||||
* "keep_children" is a boolean to determine whether or not any
|
||||
@@ -369,9 +372,10 @@ $.replace_things = function(things, keep_children, reveal, stubs) {
|
||||
* animate the transition from old to new. */
|
||||
var midcol = $(".midcol:visible:first").css("width");
|
||||
var numcol = $(".rank:visible:first").css("width");
|
||||
var self = this;
|
||||
return $.map(things, function(thing) {
|
||||
var data = thing.data;
|
||||
var existing = $.things(data.id);
|
||||
var existing = $(self).things(data.id);
|
||||
if(stubs)
|
||||
existing = existing.filter(".stub");
|
||||
existing.after($.unsafe(data.content));
|
||||
|
||||
@@ -56,7 +56,7 @@ function post_form(form, where, statusfunc, nametransformfunc, block) {
|
||||
function get_form_fields(form, fields) {
|
||||
fields = fields || {};
|
||||
/* consolidate the form's inputs for submission */
|
||||
$(form).find("select, input, textarea").not(".gray").each(function() {
|
||||
$(form).find("select, input, textarea").not(".gray, :disabled").each(function() {
|
||||
if (($(this).attr("type") != "radio" &&
|
||||
$(this).attr("type") != "checkbox") ||
|
||||
$(this).attr("checked"))
|
||||
@@ -158,7 +158,15 @@ function toggle_label (elem, callback, cancelback) {
|
||||
function toggle(elem, callback, cancelback) {
|
||||
var self = $(elem).parent().andSelf().filter(".option");
|
||||
var sibling = self.removeClass("active")
|
||||
.siblings().addClass("active").get(0);
|
||||
.siblings().addClass("active").get(0);
|
||||
|
||||
/*
|
||||
var self = $(elem).siblings().andSelf();
|
||||
var sibling = self.filter(":hidden").debug();
|
||||
self = self.filter(":visible").removeClass("active");
|
||||
sibling = sibling.addClass("active").get(0);
|
||||
*/
|
||||
|
||||
if(cancelback && !sibling.onclick) {
|
||||
sibling.onclick = function() {
|
||||
return toggle(sibling, cancelback, callback);
|
||||
@@ -238,10 +246,7 @@ function get_organic(elem, next) {
|
||||
/* links */
|
||||
|
||||
function linkstatus(form) {
|
||||
var title = $(form).find("#title").attr("value");
|
||||
if(title)
|
||||
return reddit.status_msg.submitting;
|
||||
return reddit.status_msg.fetching;
|
||||
return reddit.status_msg.submitting;
|
||||
};
|
||||
|
||||
|
||||
@@ -271,22 +276,8 @@ function unfriend(user_name, container_name, type) {
|
||||
}
|
||||
};
|
||||
|
||||
function show_media(obj) {
|
||||
obj = $.unsafe(obj);
|
||||
return function(elem) {
|
||||
var where = $(elem).thing().find(".embededmedia");
|
||||
if (where.length)
|
||||
where.show().html(obj);
|
||||
else
|
||||
$(elem).new_thing_child('<div class="embededmedia">' + obj + '</div>');
|
||||
}
|
||||
};
|
||||
|
||||
function cancelMedia(elem) {
|
||||
return cancelToggleForm(elem, ".embededmedia", ".media-button");
|
||||
};
|
||||
|
||||
function share(elem) {
|
||||
$.request("new_captcha");
|
||||
$(elem).new_thing_child($(".sharelink:first").clone(true)
|
||||
.attr("id", "sharelink_" + $(elem).thing_id()),
|
||||
false);
|
||||
@@ -299,56 +290,12 @@ function cancelShare(elem) {
|
||||
|
||||
/* Comment generation */
|
||||
function helpon(elem) {
|
||||
$(elem).parents("form:first").children(".markhelp:first").show();
|
||||
$(elem).parents(".usertext-edit:first").children(".markhelp:first").show();
|
||||
};
|
||||
function helpoff(elem) {
|
||||
$(elem).parents("form:first").children(".markhelp:first").hide();
|
||||
$(elem).parents(".usertext-edit:first").children(".markhelp:first").hide();
|
||||
};
|
||||
|
||||
|
||||
function chkcomment(form) {
|
||||
var entry = $(form).find("textarea");
|
||||
if( entry.hasClass("gray") || !entry.attr("value") ) {
|
||||
return false;
|
||||
} else if(form.replace.value)
|
||||
return post_form(form, 'editcomment', null, null, true);
|
||||
else
|
||||
return post_form(form, 'comment', null, null, true);
|
||||
};
|
||||
|
||||
function comment_edit(elem) {
|
||||
return $(".commentreply:first").clone(true)
|
||||
.find("button[name=cancel]").show().end()
|
||||
.attr("id", "commentreply_" + $(elem).thing_id());
|
||||
};
|
||||
|
||||
function reply(elem) {
|
||||
$(elem).new_thing_child(comment_edit(elem))
|
||||
.find('textarea:first').focus();
|
||||
};
|
||||
|
||||
function editcomment(elem) {
|
||||
var comment = $(elem).thing();
|
||||
var thing_name = comment.thing_id();
|
||||
var edit = comment_edit(elem);
|
||||
var content = comment.find(".edit-body:first").html();
|
||||
content = decodeURIComponent(content.replace(/\+/g, " "));
|
||||
edit.prependTo(comment)
|
||||
.hide()
|
||||
.find("button[name=comment]").hide().end()
|
||||
.find("button[name=edit]").show().end()
|
||||
.find("textarea")
|
||||
.attr("value", content)
|
||||
.removeClass("gray")
|
||||
edit.attr("parent").value = thing_name;
|
||||
edit.attr("replace").value = 1;
|
||||
|
||||
comment.children(".midcol, .entry").hide();
|
||||
edit.find("textarea:first").focus();
|
||||
edit.show();
|
||||
};
|
||||
|
||||
|
||||
function hidecomment(elem) {
|
||||
$(elem).thing().hide()
|
||||
.find(".noncollapsed:first, .midcol:first, .child:first").hide().end()
|
||||
@@ -364,15 +311,6 @@ function showcomment(elem) {
|
||||
return false;
|
||||
};
|
||||
|
||||
function cancelReply(elem) {
|
||||
var on_hide = function(form) {
|
||||
$.things($(form).attr("parent").value)
|
||||
.children(".midcol, .entry").show();
|
||||
};
|
||||
return cancelToggleForm(elem, ".commentreply", ".reply-button", on_hide);
|
||||
};
|
||||
|
||||
|
||||
function morechildren(form, link_id, children, depth) {
|
||||
$(form).html(reddit.status_msg.loading)
|
||||
.css("color", "red");
|
||||
@@ -650,6 +588,304 @@ function register(elem) {
|
||||
return post_user(this, "register");
|
||||
};
|
||||
|
||||
/***submit stuff***/
|
||||
function fetch_title() {
|
||||
var url_field = $("#url-field");
|
||||
var error = url_field.find(".NO_URL");
|
||||
var status = url_field.find(".title-status");
|
||||
var url = $("#url").val();
|
||||
if (url) {
|
||||
status.show().text("loading...");
|
||||
error.hide();
|
||||
$.request("fetch_title", {url: url});
|
||||
}
|
||||
else {
|
||||
status.hide();
|
||||
error.show().text("a url is required");
|
||||
}
|
||||
}
|
||||
|
||||
/**** sr completing ****/
|
||||
function sr_cache() {
|
||||
if (!$.defined(reddit.sr_cache)) {
|
||||
reddit.sr_cache = new Array();
|
||||
}
|
||||
return reddit.sr_cache;
|
||||
}
|
||||
|
||||
function highlight_reddit(item) {
|
||||
$("#sr-drop-down").children('.sr-selected').removeClass('sr-selected');
|
||||
if (item) {
|
||||
$(item).addClass('sr-selected');
|
||||
}
|
||||
}
|
||||
|
||||
function update_dropdown(sr_names) {
|
||||
var drop_down = $("#sr-drop-down");
|
||||
if (!sr_names.length) {
|
||||
drop_down.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var first_row = drop_down.children(":first");
|
||||
first_row.removeClass('sr-selected');
|
||||
drop_down.children().remove();
|
||||
|
||||
$.each(sr_names, function(i) {
|
||||
if (i > 10) return;
|
||||
var name = sr_names[i];
|
||||
var new_row = first_row.clone();
|
||||
new_row.text(name);
|
||||
drop_down.append(new_row);
|
||||
});
|
||||
|
||||
drop_down.show();
|
||||
}
|
||||
|
||||
function sr_search(query) {
|
||||
query = query.toLowerCase();
|
||||
var cache = sr_cache();
|
||||
if (!cache[query]) {
|
||||
$.request('search_reddit_names', {query: query},
|
||||
function (r) {
|
||||
cache[query] = r['names'];
|
||||
update_dropdown(r['names']);
|
||||
});
|
||||
}
|
||||
else {
|
||||
update_dropdown(cache[query]);
|
||||
}
|
||||
}
|
||||
|
||||
function sr_name_up(e) {
|
||||
var new_sr_name = $("#sr-autocomplete").val();
|
||||
var old_sr_name = window.old_sr_name || '';
|
||||
window.old_sr_name = new_sr_name;
|
||||
|
||||
if (new_sr_name == '') {
|
||||
hide_sr_name_list();
|
||||
}
|
||||
else if (e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 9) {
|
||||
}
|
||||
else if (e.keyCode == 27 && reddit.orig_sr) {
|
||||
$("#sr-autocomplete").val(reddit.orig_sr);
|
||||
hide_sr_name_list();
|
||||
}
|
||||
else if (new_sr_name != old_sr_name) {
|
||||
reddit.orig_sr = new_sr_name;
|
||||
sr_search($("#sr-autocomplete").val());
|
||||
}
|
||||
}
|
||||
|
||||
function sr_name_down(e) {
|
||||
var input = $("#sr-autocomplete");
|
||||
|
||||
if (e.keyCode == 38 || e.keyCode == 40) {
|
||||
var dir = e.keyCode == 38 && 'up' || 'down';
|
||||
|
||||
var cur_row = $("#sr-drop-down .sr-selected:first");
|
||||
var first_row = $("#sr-drop-down .sr-name-row:first");
|
||||
var last_row = $("#sr-drop-down .sr-name-row:last");
|
||||
|
||||
var new_row = null;
|
||||
if (dir == 'down') {
|
||||
if (!cur_row.length) new_row = first_row;
|
||||
else if (cur_row.get(0) == last_row.get(0)) new_row = null;
|
||||
else new_row = cur_row.next(':first');
|
||||
}
|
||||
else {
|
||||
if (!cur_row.length) new_row = last_row;
|
||||
else if (cur_row.get(0) == first_row.get(0)) new_row = null;
|
||||
else new_row = cur_row.prev(':first');
|
||||
}
|
||||
highlight_reddit(new_row);
|
||||
if (new_row) {
|
||||
input.val($.trim(new_row.text()));
|
||||
}
|
||||
else {
|
||||
input.val(reddit.orig_sr);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (e.keyCode == 13) {
|
||||
hide_sr_name_list();
|
||||
input.parents("form").submit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hide_sr_name_list(e) {
|
||||
$("#sr-drop-down").hide();
|
||||
}
|
||||
|
||||
function sr_dropdown_mdown(row) {
|
||||
reddit.sr_mouse_row = row; //global
|
||||
return false;
|
||||
}
|
||||
|
||||
function sr_dropdown_mup(row) {
|
||||
if (reddit.sr_mouse_row == row) {
|
||||
var name = $(row).text();
|
||||
$("#sr-autocomplete").val(name);
|
||||
$("#sr-drop-down").hide();
|
||||
}
|
||||
}
|
||||
|
||||
function set_sr_name(link) {
|
||||
var name = $(link).text();
|
||||
$("#sr-autocomplete").trigger('focus').val(name);
|
||||
}
|
||||
|
||||
/*** tabbed pane stuff ***/
|
||||
function select_form_tab(elem, to_show, to_hide) {
|
||||
//change the menu
|
||||
var link_parent = $(elem).parent();
|
||||
link_parent
|
||||
.addClass('selected')
|
||||
.siblings().removeClass('selected');
|
||||
|
||||
//swap content and enable/disable form elements
|
||||
var content = link_parent.parent('ul').next('.formtabs-content');
|
||||
content.find(to_show)
|
||||
.show()
|
||||
.find(":input").removeAttr("disabled").end();
|
||||
content.find(to_hide)
|
||||
.hide()
|
||||
.find(":input").attr("disabled", true);
|
||||
}
|
||||
|
||||
/**** expando stuff ********/
|
||||
function expando_cache() {
|
||||
if (!$.defined(reddit.thing_child_cache)) {
|
||||
reddit.thing_child_cache = new Array();
|
||||
}
|
||||
return reddit.thing_child_cache;
|
||||
}
|
||||
|
||||
function expando_child(elem) {
|
||||
var child_cache = expando_cache();
|
||||
var thing = $(elem).thing();
|
||||
|
||||
//swap button
|
||||
var button = thing.find(".expando-button");
|
||||
button
|
||||
.addClass("expanded")
|
||||
.removeClass("collapsed")
|
||||
.get(0).onclick = function() {unexpando_child(elem)};
|
||||
|
||||
//load content
|
||||
var expando = thing.find(".expando");
|
||||
var key = thing.thing_id() + "_cache";
|
||||
if (!child_cache[key]) {
|
||||
$.request("expando",
|
||||
{"link_id":thing.thing_id()},
|
||||
function(r) {
|
||||
child_cache[key] = r;
|
||||
expando.html($.unsafe(r));
|
||||
},
|
||||
false, "html");
|
||||
}
|
||||
else {
|
||||
expando.html($.unsafe(child_cache[key]));
|
||||
}
|
||||
expando.show();
|
||||
}
|
||||
|
||||
function unexpando_child(elem) {
|
||||
var thing = $(elem).thing();
|
||||
var button = thing.find(".expando-button");
|
||||
button
|
||||
.addClass("collapsed")
|
||||
.removeClass("expanded")
|
||||
.get(0).onclick = function() {expando_child(elem)};
|
||||
|
||||
thing.find(".expando").hide().empty();
|
||||
}
|
||||
|
||||
/******* editting comments *********/
|
||||
function show_edit_usertext(form) {
|
||||
var edit = form.find(".usertext-edit");
|
||||
var body = form.find(".usertext-body");
|
||||
var textarea = edit.find('div > textarea');
|
||||
|
||||
//max of the height of the content or the min values from the css.
|
||||
var body_width = Math.max(body.children(".md").width(), 500);
|
||||
var body_height = Math.max(body.children(".md").height(), 100);
|
||||
|
||||
//we need to show the textbox first so it has dimensions
|
||||
body.hide();
|
||||
edit.show();
|
||||
|
||||
//restore original (?) css width/height. I can't explain why, but
|
||||
//this is important.
|
||||
textarea.css('width', '');
|
||||
textarea.css('height', '');
|
||||
|
||||
//if there would be scroll bars, expand the textarea to the size
|
||||
//of the rendered body text
|
||||
if (textarea.get(0).scrollHeight > textarea.height()) {
|
||||
var new_width = Math.max(body_width - 5, textarea.width());
|
||||
textarea.width(new_width);
|
||||
edit.width(new_width);
|
||||
|
||||
var new_height = Math.max(body_height, textarea.height());
|
||||
textarea.height(new_height);
|
||||
}
|
||||
|
||||
form
|
||||
.find(".cancel, .save").show().end()
|
||||
.find(".help-toggle").show().end();
|
||||
|
||||
textarea.focus();
|
||||
}
|
||||
|
||||
function hide_edit_usertext(form) {
|
||||
form
|
||||
.find(".usertext-edit").hide().end()
|
||||
.find(".usertext-body").show().end()
|
||||
.find(".cancel, .save").hide().end()
|
||||
.find(".help-toggle").hide().end()
|
||||
.find(".markhelp").hide().end()
|
||||
}
|
||||
|
||||
function comment_reply_for_elem(elem) {
|
||||
elem = $(elem);
|
||||
var thing = elem.thing();
|
||||
var thing_id = elem.thing_id();
|
||||
//try to find a previous form
|
||||
var form = thing.find(".child .usertext:first");
|
||||
if (!form.length || form.parent().thing_id() != thing.thing_id()) {
|
||||
form = $(".usertext.cloneable:first").clone(true);
|
||||
elem.new_thing_child(form);
|
||||
form.attr("thing_id").value = thing_id;
|
||||
form.attr("id", "commentreply_" + thing_id);
|
||||
form.find(".error").hide();
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
function edit_usertext(elem) {
|
||||
show_edit_usertext($(elem).thing().find(".usertext:first"));
|
||||
}
|
||||
|
||||
function cancel_usertext(elem) {
|
||||
hide_edit_usertext($(elem).thing().find(".usertext:first"));
|
||||
}
|
||||
|
||||
function save_usertext(elem) {
|
||||
}
|
||||
|
||||
function reply(elem) {
|
||||
var form = comment_reply_for_elem(elem);
|
||||
//show the right buttons
|
||||
show_edit_usertext(form);
|
||||
//re-show the whole form if required
|
||||
form.show();
|
||||
//update the cancel button to call the toggle button's click
|
||||
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) {
|
||||
@@ -659,7 +895,8 @@ function populate_click_gadget() {
|
||||
clicked = $.uniq(clicked, 5);
|
||||
clicked.sort();
|
||||
|
||||
$.request('gadget/click/' + clicked.join(','), undefined, undefined, undefined, true);
|
||||
$.request('gadget/click/' + clicked.join(','), undefined, undefined,
|
||||
undefined, "json", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -809,4 +1046,3 @@ $(function() {
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
BIN
r2/r2/public/static/poll-collapsed-hover.png
Normal file
|
After Width: | Height: | Size: 230 B |
BIN
r2/r2/public/static/poll-collapsed.png
Normal file
|
After Width: | Height: | Size: 228 B |
BIN
r2/r2/public/static/poll-expanded-hover.png
Normal file
|
After Width: | Height: | Size: 220 B |
BIN
r2/r2/public/static/poll-expanded.png
Normal file
|
After Width: | Height: | Size: 217 B |
BIN
r2/r2/public/static/rightarrow.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
r2/r2/public/static/throbber.gif
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
r2/r2/public/static/vid-collapsed-hover.png
Normal file
|
After Width: | Height: | Size: 454 B |
BIN
r2/r2/public/static/vid-collapsed.png
Normal file
|
After Width: | Height: | Size: 376 B |
BIN
r2/r2/public/static/vid-expanded-hover.png
Normal file
|
After Width: | Height: | Size: 442 B |
BIN
r2/r2/public/static/vid-expanded.png
Normal file
|
After Width: | Height: | Size: 356 B |
@@ -23,7 +23,35 @@
|
||||
<%! from r2.lib.template_helpers import static %>
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
${captchagen(thing.iden, thing.error)}
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
##${captchagen(thing.iden, thing.error)}
|
||||
|
||||
${rounded_captcha()}
|
||||
|
||||
<%def name="captcha_basics(iden='')">
|
||||
<%
|
||||
iden = getattr(thing, "iden", iden)
|
||||
%>
|
||||
<input name="iden" value="${iden}" type="hidden"/>
|
||||
|
||||
<img class="capimage"
|
||||
alt="i wonder if these things even work"
|
||||
%if hasattr(thing, "iden"):
|
||||
src="/captcha/${thing.iden}.png"
|
||||
%else:
|
||||
src="${static('kill.png')}"
|
||||
%endif
|
||||
/>
|
||||
</%def>
|
||||
|
||||
<%def name="rounded_captcha()">
|
||||
<%utils:round_field title="${_('are you human?')}" description="${_('(sorry)')}" css_class="captcha">
|
||||
${captcha_basics()}
|
||||
<input name="captcha" class="captcha" type="text" />
|
||||
${error_field("BAD_CAPTCHA", "captcha")}
|
||||
</%utils:round_field>
|
||||
</%def>
|
||||
|
||||
<%def name="captchagen(iden, error='', tabulate=False, tabular = True, size=60, label=True, show_error = True)">
|
||||
%if tabulate:
|
||||
@@ -34,14 +62,7 @@ ${captchagen(thing.iden, thing.error)}
|
||||
<td></td>
|
||||
<td>
|
||||
%endif
|
||||
<img class="capimage"
|
||||
alt="i wonder if these things even work"
|
||||
%if iden:
|
||||
src="/captcha/${iden}.png"
|
||||
%else:
|
||||
src="${static('kill.png')}"
|
||||
%endif
|
||||
/>
|
||||
${captcha_basics(iden)}
|
||||
%if tabular:
|
||||
</td>
|
||||
</tr>
|
||||
@@ -57,8 +78,7 @@ ${captchagen(thing.iden, thing.error)}
|
||||
</td>
|
||||
<td>
|
||||
%endif
|
||||
<input name="iden" type="hidden" value="${iden}"/>
|
||||
<input class="captcha cap-text" id="captcha_"
|
||||
<input class="captcha cap-text" id="captcha_"
|
||||
name="captcha" type="text" size="${size}" />
|
||||
<script type="text/javascript">
|
||||
emptyInput($('input.captcha'), "${_('type the letters from the image above')}");
|
||||
@@ -70,7 +90,7 @@ ${captchagen(thing.iden, thing.error)}
|
||||
</span>
|
||||
%endif
|
||||
%if show_error:
|
||||
${error_field("BAD_CAPTCHA", "span")}
|
||||
${error_field("BAD_CAPTCHA", "captcha")}
|
||||
%endif
|
||||
%if tabular:
|
||||
</td>
|
||||
@@ -80,3 +100,4 @@ ${captchagen(thing.iden, thing.error)}
|
||||
</table>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -102,20 +102,7 @@ ${parent.Child(not thing.collapsed)}
|
||||
</%def>
|
||||
|
||||
<%def name="commentBody()">
|
||||
%if c.user_is_admin or not thing.deleted:
|
||||
${unsafe(safemarkdown(thing.body, nofollow=thing.nofollow,
|
||||
target=thing.target))}
|
||||
%else:
|
||||
<div class="gray md">
|
||||
${_("[deleted]")}
|
||||
</div>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="commentText()">
|
||||
%if c.user == thing.author:
|
||||
${edit_comment_filter(thing.body)}
|
||||
%endif
|
||||
${parent.commentBody()}
|
||||
</%def>
|
||||
|
||||
<%def name="arrows()">
|
||||
@@ -141,9 +128,9 @@ ${parent.midcol()}
|
||||
${parent.bylink_button(_("parent"), thing.parent_permalink)}
|
||||
</li>
|
||||
%endif
|
||||
%if c.user_is_loggedin and thing.author.name == c.user.name:
|
||||
%if thing.usertext.editable:
|
||||
<li>
|
||||
${parent.simple_button(_("edit"), "editcomment")}
|
||||
${parent.simple_button(_("edit"), "edit_usertext")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
@@ -151,9 +138,7 @@ ${parent.midcol()}
|
||||
${parent.buttons()}
|
||||
%if not c.profilepage and thing.can_reply:
|
||||
<li>
|
||||
${parent.toggle_button("reply-button", _("reply {verb}"),
|
||||
_("cancel"), "reply", "cancelReply",
|
||||
login_required = True)}
|
||||
${parent.simple_button(_("reply"), "reply")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
|
||||
@@ -37,12 +37,10 @@ ${self.tagline(True)}
|
||||
</%def>
|
||||
|
||||
<%def name="commentBody()">
|
||||
${thing.usertext.render(style="html")}
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="commentText()">
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
<% collapse = thing.collapsed %>
|
||||
<div class="collapsed" ${(not collapse and "style='display:none'" or "")}>
|
||||
@@ -52,18 +50,7 @@ ${self.tagline(True)}
|
||||
<p class="tagline">
|
||||
${self.tagline()}
|
||||
</p>
|
||||
<%
|
||||
if c.user_is_admin and thing.deleted:
|
||||
colorclass = 'grayed'
|
||||
elif c.focal_comment and c.focal_comment == thing._id36:
|
||||
colorclass = 'border'
|
||||
else:
|
||||
colorclass = ''
|
||||
%>
|
||||
<div class="commentbody ${colorclass}">
|
||||
${self.commentBody()}
|
||||
</div>
|
||||
<div class="edit-body">${self.commentText()}</div>
|
||||
${self.commentBody()}
|
||||
<ul class="flat-list buttons">
|
||||
${self.buttons()}
|
||||
</ul>
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
## The contents of this file are subject to the Common Public Attribution
|
||||
## License Version 1.0. (the "License"); you may not use this file except in
|
||||
## compliance with the License. You may obtain a copy of the License at
|
||||
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
|
||||
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
|
||||
## software over a computer network and provide for limited attribution for the
|
||||
## Original Developer. In addition, Exhibit A has been modified to be consistent
|
||||
## with Exhibit B.
|
||||
##
|
||||
## Software distributed under the License is distributed on an "AS IS" basis,
|
||||
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
|
||||
## the specific language governing rights and limitations under the License.
|
||||
##
|
||||
## The Original Code is Reddit.
|
||||
##
|
||||
## The Original Developer is the Initial Developer. The Initial Developer of
|
||||
## the Original Code is CondeNet, Inc.
|
||||
##
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2009
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
<form id="commentreply_${thing.link_name}"
|
||||
${'' if thing.link_name else "style='display:none'"}
|
||||
onsubmit="return chkcomment(this);" action="#"
|
||||
method="get" class="commentreply">
|
||||
%if thing.link_name:
|
||||
<input type="hidden" name="isroot" value="1" />
|
||||
%endif
|
||||
<input type="hidden" name="replace" value="" />
|
||||
<input type="hidden" name="parent" value="${thing.link_name}" />
|
||||
|
||||
${error_field("BAD_COMMENT")}
|
||||
${error_field("DELETED_COMMENT")}
|
||||
${error_field("COMMENT_TOO_LONG")}
|
||||
${error_field("RATELIMIT")}
|
||||
## I know we don't need this div. You know we don't need this div. IE
|
||||
## begs to differ.
|
||||
<div>
|
||||
<textarea name="comment" rows="7">
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<button type="submit" class="btn"
|
||||
name="comment">${_("comment {verb}")}
|
||||
</button>
|
||||
<button style="display:none;" name="edit"
|
||||
type="submit" class="btn">${_( "edit")}</button>
|
||||
<button class="btn" name="cancel" style="display:none;"
|
||||
onclick="cancelReply(this);return false">
|
||||
${_("cancel")}</button>
|
||||
<span class="status error"></span>
|
||||
</div>
|
||||
<div class="help-toggle toggle">
|
||||
<a href="#" class="option active"
|
||||
onclick='return toggle(this, helpon, helpoff)'>
|
||||
${_("help")}
|
||||
</a>
|
||||
<a href="#" class="option">${_("hide help")}</a>
|
||||
</div>
|
||||
<div class="clearleft"></div>
|
||||
<script type="text/javascript">
|
||||
var form = $("#commentreply_${thing.link_name}").find("textarea");
|
||||
emptyInput(form, "${_('enter a comment here')}");
|
||||
</script>
|
||||
|
||||
<div class="markhelp" style="display: none">
|
||||
<table class="help">
|
||||
<tr style="background-color: #ffff99; text-align: center">
|
||||
<td><em>${_( "you type:")}</em></td>
|
||||
<td><em>${_( "you see:")}</em></td></tr>
|
||||
<tr><td>*${_( "italics")}*</td>
|
||||
<td><em>${_( "italics")}</em></td></tr>
|
||||
<tr><td>**${_( "bold")}**</td>
|
||||
<td><b>${_( "bold")}</b></td></tr>
|
||||
<tr><td>[reddit!](http://reddit.com)</td>
|
||||
<td><a href="http://reddit.com">reddit!</a></td></tr>
|
||||
<tr><td>* ${_( "item")} 1<br/>
|
||||
* ${_( "item")} 2<br/>
|
||||
* ${_( "item")} 3</td>
|
||||
<td><ul><li>${_( "item")} 1</li>
|
||||
<li>${_( "item")} 2</li>
|
||||
<li>${_( "item")} 3</li></ul></td></tr>
|
||||
<tr><td>> ${_( "quoted text")}</td>
|
||||
<td><blockquote>${_( "quoted text" )}</blockquote></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,22 +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.
|
||||
################################################################################
|
||||
|
||||
@@ -1,22 +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.
|
||||
################################################################################
|
||||
|
||||
@@ -85,8 +85,8 @@ function update_title(elem) {
|
||||
</td>
|
||||
<td class="note" id="note_name">
|
||||
%if not thing.site:
|
||||
${error_field("SUBREDDIT_EXISTS", "span")}
|
||||
${error_field("BAD_SR_NAME", "span")}
|
||||
${error_field("SUBREDDIT_EXISTS", "name")}
|
||||
${error_field("BAD_SR_NAME", "name")}
|
||||
<span class="gray" id="example_name">
|
||||
${_("no spaces, e.g. slashdot")}
|
||||
</span>
|
||||
@@ -106,8 +106,8 @@ function update_title(elem) {
|
||||
%endif
|
||||
</td>
|
||||
<td class="note" id="note_title">
|
||||
${error_field("NO_TITLE", "span")}
|
||||
${error_field("TITLE_TOO_LONG", "span")}
|
||||
${error_field("NO_TEXT", "title")}
|
||||
${error_field("TOO_LONG", "title")}
|
||||
%if not thing.site:
|
||||
<span class="gray" id="example_title">
|
||||
${_("e.g. slashdot: news for nerds, stuff that matters")}
|
||||
@@ -129,7 +129,7 @@ function update_title(elem) {
|
||||
</textarea>
|
||||
</td>
|
||||
<td class="note" id="note_description">
|
||||
${error_field("DESC_TOO_LONG", "span")}
|
||||
${error_field("TOO_LONG", "description")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -199,8 +199,8 @@ function update_title(elem) {
|
||||
<td class="note" id="note_domain">
|
||||
<a id="example_domain" href="#"
|
||||
onclick="$('#domainhelp').slideDown(); $('#example_domain').fadeOut(); return false;">what's this?</a>
|
||||
${error_field("BAD_CNAME", "span")}
|
||||
${error_field("USED_CNAME", "span")}
|
||||
${error_field("BAD_CNAME", "domain")}
|
||||
${error_field("USED_CNAME", "domain")}
|
||||
</td>
|
||||
</tr>
|
||||
%if thing.site and thing.site.can_change_stylesheet(c.user) and not g.css_killswitch:
|
||||
@@ -265,5 +265,5 @@ function update_title(elem) {
|
||||
>${text}</button>
|
||||
 
|
||||
<span class="status error"></span>
|
||||
${error_field("RATELIMIT", "span")}
|
||||
${error_field("RATELIMIT", "ratelimit")}
|
||||
</div>
|
||||
|
||||
@@ -20,93 +20,57 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
<%namespace file="utils.html" import="error_field"/
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
<%!
|
||||
from r2.lib.pages import UserText
|
||||
%>
|
||||
|
||||
|
||||
%if thing.title:
|
||||
<h1>${thing.title}</h1>
|
||||
%endif
|
||||
|
||||
%if not thing.success:
|
||||
<form onsubmit="return post_form(this, 'feedback');"
|
||||
class="pretty-form"
|
||||
%if action:
|
||||
action="/${thing.action}" \
|
||||
%else:
|
||||
action=''
|
||||
%endif
|
||||
action="/${thing.action}"
|
||||
method="post" class="content" id="feedback">
|
||||
<p class="success" class="error">${thing.success or ''}
|
||||
</p>
|
||||
|
||||
%if c.user_is_loggedin:
|
||||
<input type="hidden" name="whodunnit" value="${c.user.name}" />
|
||||
<input type="hidden" name="whodunnit" value="${thing.name}" />
|
||||
%endif
|
||||
<input type="hidden" name="reason" value="${thing.action}" />
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<th><label for="name">${_("your name")}</label></th>
|
||||
<td>
|
||||
<input id="name" name="name" type="text"
|
||||
value="${thing.name or (c.user_is_loggedin and c.user.name or '')}"
|
||||
size="30"/>
|
||||
${error_field("NO_NAME", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><label for="email">${_("email")}</label></th>
|
||||
<td>
|
||||
%if c.user_is_loggedin and hasattr(c.user, "email"):
|
||||
<input id="email" name="email" value="${c.user.email}"
|
||||
type="text" disabled=disabled size="30"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="replyto">${_("reply to")}</label>
|
||||
</th>
|
||||
<td>
|
||||
%if thing.replyto:
|
||||
<input id="replyto" name="replyto" type="text"
|
||||
value="${thing.email}"
|
||||
size="30"/>
|
||||
%else:
|
||||
<input id="replyto" name="replyto" class="gray"
|
||||
value="${_('optional')}" type="text"
|
||||
size="30"/>
|
||||
%endif
|
||||
${error_field("BAD_EMAILS", "span")}
|
||||
%else:
|
||||
%if thing.email:
|
||||
<input id="email" name="email" type="text"
|
||||
value="${thing.email}" size="30"/>
|
||||
%else:
|
||||
<input id="email" type="text" name="email" size="30"
|
||||
value="${ _('put your email here')}" class="gray"/>
|
||||
%endif
|
||||
%endif
|
||||
<span class="little gray">${_("(only used to reply)")}</span>
|
||||
</td><td>
|
||||
${error_field("NO_EMAIL", "span")}
|
||||
</td>
|
||||
</tr><tr>
|
||||
<th valign="top">
|
||||
<label for="message">${_("message")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea id="personal" name="message" rows="5" cols="60">${thing.message or ''}</textarea>
|
||||
${error_field("NO_MESSAGE", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
</table>
|
||||
<div style="margin: 10px 0 20px 0">
|
||||
<button class="btn" type="submit">${_("send")}</button>
|
||||
<span class="status"></span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
%endif
|
||||
<input type="hidden" name="reason" value="${thing.action}" />
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('your name')}">
|
||||
<input type="text" name="name" value="${thing.name}"/>
|
||||
${error_field("NO_NAME", "name", "span")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('email')}" description="${_('(only used to reply)')}">
|
||||
<input type="text" name="email" value="${thing.email}"/>
|
||||
${error_field("BAD_EMAILS", "email", "span")}
|
||||
${error_field("NO_EMAIL", "email", "span")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('message')}">
|
||||
${UserText(None, have_form = False, creating = True).render()}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<button class="btn" type="submit">${_("send")}</button>
|
||||
|
||||
<span class="status"></span>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -61,6 +61,13 @@
|
||||
</a>
|
||||
</%def>
|
||||
|
||||
<%def name="bottom_buttons()">
|
||||
<ul class="flat-list buttons">
|
||||
${self.buttons()}
|
||||
${self.admintagline()}
|
||||
</ul>
|
||||
</%def>
|
||||
|
||||
<%def name="entry()">
|
||||
<p class="title">
|
||||
<%call expr="make_link('title', 'title')">
|
||||
@@ -69,13 +76,50 @@
|
||||
 
|
||||
${self.domain()}
|
||||
</p>
|
||||
|
||||
##the expando button
|
||||
%if thing.link_child and not thing.link_child.expand:
|
||||
<div class="expando-button collapsed
|
||||
${thing.link_child.css_style}"
|
||||
onclick="expando_child(this)"></div>
|
||||
%endif
|
||||
|
||||
<p class="tagline">
|
||||
${self.tagline()}
|
||||
</p>
|
||||
<ul class="flat-list buttons">
|
||||
${self.buttons()}
|
||||
${self.admintagline()}
|
||||
</ul>
|
||||
|
||||
<%
|
||||
child_content = ""
|
||||
if thing.link_child and thing.link_child.load:
|
||||
child_content = unsafe(thing.link_child.content())
|
||||
expand = thing.link_child and thing.link_child.expand
|
||||
%>
|
||||
|
||||
##if we're not on a permalink page we'll render the buttons on top
|
||||
%if not expand:
|
||||
${bottom_buttons()}
|
||||
%endif
|
||||
|
||||
<div class="expando" ${"style='display: none'" if not expand else ""}>
|
||||
%if expand:
|
||||
${child_content}
|
||||
%else:
|
||||
<span class="error">loading...</span>
|
||||
%endif
|
||||
</div>
|
||||
|
||||
##if we are on a permalink page, we'll render the buttons below
|
||||
%if expand:
|
||||
${bottom_buttons()}
|
||||
%endif
|
||||
|
||||
##populate the expando cache if we have something
|
||||
%if not expand and child_content:
|
||||
<script type="text/javascript">
|
||||
var cache = expando_cache();
|
||||
cache["${thing._fullname}_cache"] = "${websafe(child_content)}";
|
||||
</script>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="subreddit()" buffered="True">
|
||||
@@ -145,6 +189,11 @@
|
||||
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)}
|
||||
@@ -171,18 +220,6 @@
|
||||
${parent.delete_or_report_buttons(delete=delete,report=report)}
|
||||
${parent.buttons(ban=ban)}
|
||||
${additional}
|
||||
${self.media_embed()}
|
||||
</%def>
|
||||
|
||||
|
||||
<%def name="media_embed()">
|
||||
%if thing.media_object:
|
||||
<li>
|
||||
${parent.toggle_button("media-button", _("watch"), _("cancel"),
|
||||
'show_media("%s")' % websafe(thing.media_object),
|
||||
"cancelMedia")}
|
||||
</li>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="thumbnail()">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
################################################################################
|
||||
<%namespace file="printable.html" import="state_button, thing_css_class" />
|
||||
|
||||
<div class="raisedbox linkinfo ${thing_css_class(thing.a)}">
|
||||
<div class="raisedbox linkinfo">
|
||||
<table class="details">
|
||||
%if not thing.a.is_self:
|
||||
<tr><th>${_("toolbar link")}</th>
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
<input value="${user}" name="user" id="user_${op}"
|
||||
type="text" maxlength="20"/>
|
||||
%if register:
|
||||
${error_field("BAD_USERNAME", kind="span")}
|
||||
${error_field("USERNAME_TAKEN", kind="span")}
|
||||
${error_field("BAD_USERNAME", "user", kind="span")}
|
||||
${error_field("USERNAME_TAKEN", "user", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%if register:
|
||||
@@ -79,7 +79,7 @@
|
||||
<input value="" name="email" id="email_${op}"
|
||||
type="text" maxlength="50"/>
|
||||
%if register:
|
||||
${error_field("BAD_EMAILS", kind="span")}
|
||||
${error_field("BAD_EMAILS", "email", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%endif
|
||||
@@ -88,9 +88,9 @@
|
||||
<input id="passwd_${op}" name="passwd" type="password"
|
||||
maxlength="20"/>
|
||||
%if register:
|
||||
${error_field("BAD_PASSWORD", kind="span")}
|
||||
${error_field("BAD_PASSWORD", "passwd", kind="span")}
|
||||
%else:
|
||||
${error_field("WRONG_PASSWORD", kind="span")}
|
||||
${error_field("WRONG_PASSWORD", "passwd", kind="span")}
|
||||
%endif
|
||||
</li>
|
||||
%if register:
|
||||
@@ -98,12 +98,12 @@
|
||||
<label for="passwd2_${op}">${_('verify password')}:</label>
|
||||
<input name="passwd2" id="passwd2_${op}"
|
||||
type="password" maxlength="20" />
|
||||
${error_field("BAD_PASSWORD_MATCH", kind="span")}
|
||||
${error_field("BAD_PASSWORD_MATCH", "passwd2", kind="span")}
|
||||
</li>
|
||||
<li>
|
||||
${captchagen('', tabulate=True, label=False, size=30)}
|
||||
${error_field("DRACONIAN", kind="span")}
|
||||
${error_field("RATELIMIT", kind="span")}
|
||||
<% iden = hasattr(thing, "captcha") and thing.captcha.iden or '' %>
|
||||
${captchagen(iden, tabulate=True, label=False, size=30)}
|
||||
${error_field("RATELIMIT", "ratelimit")}
|
||||
</li>
|
||||
%endif
|
||||
<li>
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
<input name="user" type="text" maxlength="20" tabindex="1"/>
|
||||
<input name="passwd" type="password" maxlength="20" tabindex="2"/>
|
||||
|
||||
${error_field("WRONG_PASSWORD", "div")}
|
||||
${error_field("WRONG_PASSWORD", "passwd", "div")}
|
||||
<div class="status error"></div>
|
||||
|
||||
<div id="remember-me">
|
||||
|
||||
@@ -66,10 +66,6 @@
|
||||
${unsafe(safemarkdown(thing.body))}
|
||||
</%def>
|
||||
|
||||
<%def name="commentText()">
|
||||
${edit_comment_filter(thing.body)}
|
||||
</%def>
|
||||
|
||||
<%def name="buttons()">
|
||||
%if hasattr(thing, "was_comment"):
|
||||
<li>
|
||||
@@ -81,7 +77,7 @@ ${unsafe(safemarkdown(thing.body))}
|
||||
${parent.buttons()}
|
||||
%if c.user_is_loggedin:
|
||||
<li>
|
||||
${parent.simple_button(_("reply {verb}"), "reply")}
|
||||
${parent.simple_button(_("reply"), "reply")}
|
||||
</li>
|
||||
%endif
|
||||
%endif
|
||||
@@ -94,11 +90,7 @@ ${unsafe(safemarkdown(thing.body))}
|
||||
</p>
|
||||
<div class="body">${self.commentBody()}
|
||||
</div>
|
||||
%if thing.author == c.user:
|
||||
<div style="display: none;">${self.commentText()}\
|
||||
</div>
|
||||
%endif
|
||||
<ul class="flat-list buttons" ${(collapse and "style='display:none'" or '')}>
|
||||
<ul class="flat-list buttons" ${(collapse and "style='display:none'" or '')}>
|
||||
${self.buttons()}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -20,57 +20,48 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field, submit_form, success_field"/>
|
||||
<%namespace file="utils.html" import="error_field, submit_form"/>
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
<%!
|
||||
from r2.lib.pages import UserText
|
||||
%>
|
||||
|
||||
<h1>${_("send a message")}</h1>
|
||||
|
||||
|
||||
<%call expr="submit_form(
|
||||
onsubmit='return post_form(this, \'compose\', null, null, true)',
|
||||
method='post', _id = 'compose-message',
|
||||
action='/message/compose', _class='iform')">
|
||||
${success_field(_("your message has been delivered"), successful=thing.success)}
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="to">${_("to (username)")}</label></th>
|
||||
<td><input type="text" name="to" value="${thing.to or ''}"
|
||||
id="to" size="40"/></td>
|
||||
<td>${error_field("NO_USER", "span")}
|
||||
${error_field("USER_DOESNT_EXIST", "span")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="subject">${_("subject")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" name="subject" size="40" id="subject"\
|
||||
value="${thing.subject or ''}"/>
|
||||
</td>
|
||||
<td>${error_field("NO_SUBJECT", "span")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="message">${_("message")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<textarea id="message" name="message" rows="10" cols="40">
|
||||
${thing.message or ''}
|
||||
</textarea></td>
|
||||
<td>${error_field("NO_MSG_BODY", "span")}
|
||||
${error_field("COMMENT_TOO_LONG", "span")}</td>
|
||||
</tr>
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
<tr>
|
||||
<th>
|
||||
<label for="send"></label>
|
||||
</th>
|
||||
<td>
|
||||
<button id="send" name="send" type="submit">${_("send")}</button>
|
||||
<span id='status' class='error'></span>
|
||||
</td>
|
||||
<td id="note_send"></td></tr>
|
||||
</table>
|
||||
</%call>
|
||||
<%utils:submit_form onsubmit="return post_form(this, 'compose', null, null, true)",
|
||||
method="post", _id = "compose-message",
|
||||
action="/message/compose">
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('to')}", description="${_('(username)')}">
|
||||
<input type="text" name="to" value="${thing.to or ''}"/>
|
||||
${error_field("NO_USER", "to")}
|
||||
${error_field("USER_DOESNT_EXIST", "to")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('subject')}">
|
||||
<input type="text" name="subject" value="${thing.subject or ''}"/>
|
||||
${error_field("NO_SUBJECT", "subject", "span")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('message')}">
|
||||
${UserText(None, have_form = False, creating = True).render()}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
</div>
|
||||
|
||||
<button id="send" name="send" type="submit">${_("send")}</button>
|
||||
<span class="status"></span>
|
||||
|
||||
</%utils:submit_form>
|
||||
|
||||
@@ -38,9 +38,6 @@ load more comments <span class="gray"> (${thing.count} ${ungettext("reply",
|
||||
</span>
|
||||
</%def>
|
||||
|
||||
<%def name="commentText()">
|
||||
</%def>
|
||||
|
||||
<%def name="arrows()">
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
<span class="deepthread"><a href="${thing.parent_permalink}">${_("continue this thread")}</a></span>
|
||||
</%def>
|
||||
|
||||
<%def name="commentText()">
|
||||
</%def>
|
||||
|
||||
<%def name="arrows()">
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<%def name="js(selected = False)">
|
||||
${plain_link(thing.selected_title() if selected else thing.title,
|
||||
'/', _sr_path = False, nocname = thing.nocname,
|
||||
thing.path, _sr_path = False, nocname = True,
|
||||
_class = thing.css_class, _id = thing._id, onclick = thing.onclick)}
|
||||
</%def>
|
||||
|
||||
|
||||
@@ -111,7 +111,8 @@
|
||||
|
||||
<%def name="tabmenu()">
|
||||
%if thing:
|
||||
<ul class="tabmenu"
|
||||
<% css_class = str(thing.css_class) if thing.css_class else "" %>
|
||||
<ul class="tabmenu ${css_class}"
|
||||
${"id='%s'" % thing._id if thing._id else ""}>
|
||||
%for i, option in enumerate(thing):
|
||||
<%
|
||||
|
||||
@@ -22,90 +22,109 @@
|
||||
|
||||
<%!
|
||||
from r2.lib.strings import strings
|
||||
from r2.lib.pages import UserText
|
||||
from r2.lib.template_helpers import add_sr
|
||||
%>
|
||||
|
||||
<%namespace file="utils.html" import="error_field, submit_form, plain_link, text_with_links"/>
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
%if thing.subreddits:
|
||||
<h1>${_("submit")}</h1>
|
||||
%else:
|
||||
<h1>${_("submit to %(site)s") % dict(site=c.site.name)}</h1>
|
||||
<h1>${_("submit to reddit")}</h1>
|
||||
|
||||
<%utils:submit_form onsubmit="return post_form(this, 'submit', linkstatus, null, true)"
|
||||
action=${add_sr("/submit")},
|
||||
_class="submit content",
|
||||
_id="newlink">
|
||||
|
||||
${thing.formtabs_menu.render()}
|
||||
<div class="formtabs-content">
|
||||
|
||||
<div class="spacer">
|
||||
<div id="link-desc" class="infobar">${strings.submit_link}</div>
|
||||
<div id="text-desc" class="infobar">${strings.submit_text}</div>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('title')}" id="title-field">
|
||||
<textarea name="title" rows="2" cols="1" wrap="hard">${thing.title}</textarea>
|
||||
${error_field("NO_TEXT", "title", "span")}
|
||||
${error_field("TOO_LONG", "title", "span")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('url')}" id="url-field">
|
||||
<input name="kind" value="link" type="hidden"/>
|
||||
<input id="url" name="url" type="text" value="${thing.url}"/>
|
||||
${error_field("NO_URL", "url", "span")}
|
||||
${error_field("BAD_URL", "url", "span")}
|
||||
${error_field("ALREADY_SUB", "url", "span")}
|
||||
<div>
|
||||
<button type="button" onclick="fetch_title()">${_("suggest title")}</button>
|
||||
<span class="title-status"></span>
|
||||
</div>
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<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()}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('reddit')}" id="reddit-field">
|
||||
<div id="sr-autocomplete-area">
|
||||
<input id="sr-autocomplete" name="sr" type="text"
|
||||
autocomplete="off"
|
||||
onkeyup="sr_name_up(event)"
|
||||
onkeydown="return sr_name_down(event)"
|
||||
onblur="hide_sr_name_list()"
|
||||
value = "${thing.default_sr}" />
|
||||
<ul id="sr-drop-down">
|
||||
<li class="sr-name-row"
|
||||
onmouseover="highlight_reddit(this)"
|
||||
onmousedown="return sr_dropdown_mdown(this)"
|
||||
onmouseup="sr_dropdown_mup(this)">nothin</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
reddit.sr_cache = ${unsafe(thing.sr_searches)};
|
||||
</script>
|
||||
${error_field("SUBREDDIT_NOEXIST", "sr", "span")}
|
||||
${error_field("SUBREDDIT_REQUIRED", "sr", "span")}
|
||||
|
||||
<div id="suggested-reddits">
|
||||
<span class="">${_("popular choices")} </span>
|
||||
<ul>
|
||||
%for name in thing.subreddits:
|
||||
<li>
|
||||
<a href="#" onclick="set_sr_name(this); return false">${name}</a> 
|
||||
</li>
|
||||
%endfor
|
||||
</ul>
|
||||
</div>
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
|
||||
</div>
|
||||
|
||||
<%call expr="submit_form(onsubmit='return post_form(this, \'submit\', linkstatus, null, true)',
|
||||
action=add_sr('/submit'), _class='long-text content', _id='newlink')">
|
||||
%if not thing.subreddits:
|
||||
<input type="hidden" name="sr" value="${c.site.name}" />
|
||||
%endif
|
||||
<input type="hidden" name="then" value="${thing.then}" />
|
||||
<table>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="url">${_("url")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<input id="url" name="url" type="text" value="${thing.url}"/>
|
||||
${error_field("NO_URL", "span")}
|
||||
${error_field("BAD_URL", "span")}
|
||||
${error_field("ALREADY_SUB", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<label for="title">${_("title")}</label>
|
||||
</th>
|
||||
<td><input id="title" name="title" value="${thing.title}" type="text" />
|
||||
${error_field("NO_TITLE", "span")}
|
||||
${error_field("TITLE_TOO_LONG", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
%if thing.subreddits:
|
||||
<tr>
|
||||
<th>
|
||||
<label for="sr">${_("subreddit")}</label>
|
||||
</th>
|
||||
<td>
|
||||
<select id="sr" name="sr">
|
||||
%for sr in thing.subreddits:
|
||||
<option value="${sr}">${sr}</option>
|
||||
%endfor
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
%endif
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input id="save" name="save" type="checkbox" />
|
||||
<label for="save">${_("add to my saved sites")}</label>
|
||||
</td>
|
||||
</tr>
|
||||
%if thing.captcha:
|
||||
${thing.captcha.render()}
|
||||
%endif
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<button class="btn" type="submit">${_("submit")}</button>
|
||||
<span class="status"></span>
|
||||
${error_field("RATELIMIT", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script type="text/javascript">
|
||||
var form = $("#newlink");
|
||||
if(form.length) {
|
||||
emptyInput(form.find("#url"),
|
||||
'${unsafe(_('type "self" here to make this post refer to itself.'))}');
|
||||
emptyInput(form.find("#title"),
|
||||
"${_('enter a title, or click submit to find one automatically.')}")
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
${text_with_links(_("use reddit from your toolbar with the %(bookmarklets)s"),
|
||||
bookmarklets = (_('reddit bookmarklets'), '/bookmarklets'))}
|
||||
</p>
|
||||
</%call>
|
||||
<div class="spacer">
|
||||
<button class="btn" name="submit" value="form" type="submit" tabindex=1>${_("submit")}</button>
|
||||
<span class="status"></span>
|
||||
${error_field("RATELIMIT", "ratelimit")}
|
||||
</div>
|
||||
</%utils:submit_form>
|
||||
|
||||
<script type="text/javascript">
|
||||
var form = $("#newlink");
|
||||
if(form.length) {
|
||||
var default_menu = form.find(".${thing.default_tab}:first");
|
||||
select_form_tab(default_menu, "${thing.default_show}", "${thing.default_hide}");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
${thing.css_class and ("class='%s'" % str(thing.css_class)) or ""}>
|
||||
%endif
|
||||
|
||||
%if thing.title:
|
||||
<h1>${thing.title}</h1>
|
||||
%endif
|
||||
|
||||
${t.render() if t else ''}
|
||||
|
||||
%if thing.div:
|
||||
|
||||
@@ -21,33 +21,24 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field, success_field"/>
|
||||
${success_field(_('you should receive an email shortly'),
|
||||
successful=thing.success, hide='passform')}
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
<form id="passform" action="/api/password" method="post"
|
||||
class="content pretty-form medium-text"
|
||||
class="content"
|
||||
onsubmit="return post_form(this, 'password');">
|
||||
<h1>${_("what's my password?")}</h1>
|
||||
<p> ${_("enter your user name below to receive your login information")}</p>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="name">${_("username")}</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" id="name" name="name" />
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit" class="btn">${_("email me")}</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<span class="status"></span>
|
||||
${error_field("USER_DOESNT_EXIST", "span")}
|
||||
${error_field("NO_EMAIL_FOR_USER", "span")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>${_("enter your user name below to receive your login information")}</p>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('username')}">
|
||||
<input type="text" name="name" />
|
||||
${error_field("USER_DOESNT_EXIST", "name")}
|
||||
${error_field("NO_EMAIL_FOR_USER", "name")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">${_("email me")}</button>
|
||||
<span class="status"></span>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
|
||||
<div class="autosize">
|
||||
<div class="permamessage">
|
||||
<p style="margin:0">
|
||||
${_("you are viewing a single comment's thread")}
|
||||
</p>
|
||||
<p style="margin: 4px 0 0 0"><a href="${thing.comments_url}">${_("see the above comments")}</a></p>
|
||||
</div>
|
||||
<div class="infobar">
|
||||
${_("you are viewing a single comment's thread.")}
|
||||
<p>
|
||||
<a href="${thing.comments_url}">${_("view the rest of the comments")}</a>
|
||||
→
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -21,28 +21,36 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
<% import random %>
|
||||
<%def name="areyousure(name)">
|
||||
<tr><td>${_("are you sure?")}</td>
|
||||
<% yes, no = random.choice(((_("yes"), _("no")), (_("no"), _("yes")))) %>
|
||||
<td><input name="${name}" id="${name}yes" type="radio" value="${yes}"/>
|
||||
<% yes, no = random.choice(((_("yes"), _("no")), (_("no"), _("yes")))) %>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('are you sure?')}">
|
||||
<div class="rounded delete-field">
|
||||
<input name="${name}" id="${name}yes" type="radio" value="${yes}"/>
|
||||
<label for="${name}yes">${yes}</label>
|
||||
</td>
|
||||
<td><input name="${name}" id="${name}no" type="radio" value="${no}"/>
|
||||
|
||||
<br/>
|
||||
|
||||
<input name="${name}" id="${name}no" type="radio" value="${no}"/>
|
||||
<label for="${name}no">${no}</label>
|
||||
</td>
|
||||
</tr>
|
||||
</div>
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||
<h1>${_("delete your reddit account? hope you have a good reason.")}</h1>
|
||||
|
||||
<form action="/post/delete" method="post" class="pretty-form"
|
||||
<form action="/post/delete" method="post"
|
||||
onsubmit="${"return post_form(this, 'delete_user', function(x) {return '%s'})" % _("deleting...")}" id="pref-delete">
|
||||
<table>
|
||||
|
||||
${areyousure("areyousure1")}
|
||||
${areyousure("areyousure2")}
|
||||
${areyousure("areyousure3")}
|
||||
</table>
|
||||
|
||||
<input type="submit" class="btn" value="${_('delete')}"/>
|
||||
<span class="status"></span>
|
||||
</form>
|
||||
|
||||
@@ -21,34 +21,42 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
<form action="/post/update" method="post" class="pretty-form medium-text"
|
||||
<h1>${_("update your email or password")}</h1>
|
||||
|
||||
<form action="/post/update" method="post"
|
||||
onsubmit="return post_form(this, 'update')" id="pref-update">
|
||||
<table>
|
||||
<tr>
|
||||
<td align="right">${_("current password (required)")}:</td>
|
||||
<td><input name="curpass" type="password" /></td>
|
||||
<td>${error_field("WRONG_PASSWORD")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">${_("email")}:</td>
|
||||
<td><input name="email" type="text" value="${hasattr(c.user,'email') and c.user.email or ''}"/></td>
|
||||
<td>
|
||||
${error_field("BAD_EMAILS")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">${_("new password")}:</td>
|
||||
<td><input name="newpass" type="password" maxlength="20"/></td>
|
||||
<td>${error_field("BAD_PASSWORD")}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="right">${_("verify password")}:</td>
|
||||
<td><input name="verpass" type="password" maxlength="20"/></td>
|
||||
<td> ${error_field("BAD_PASSWORD_MATCH")}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('current password')}" description="${_('(required)')}">
|
||||
<input type="password" name="curpass" />
|
||||
${error_field("WRONG_PASSWORD", "curpass")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('email')}">
|
||||
<input type="text" name="email" value="${getattr(c.user, 'email', '')}"/>
|
||||
${error_field("BAD_EMAILS", "email")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('new password')}">
|
||||
<input type="password" name="newpass"/>
|
||||
${error_field("BAD_PASSWORD", "newpass")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('verify password')}">
|
||||
<input type="password" name="verpass"/>
|
||||
${error_field("BAD_PASSWORD_MATCH", "verpass")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn">${_('save')}</button>
|
||||
<span class="status error"></span>
|
||||
|
||||
</form>
|
||||
|
||||
@@ -305,14 +305,15 @@ thing id-${what._fullname}
|
||||
callback, cancelback,
|
||||
css_class = '', alt_css_class = '',
|
||||
reverse = False,
|
||||
login_required = 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">
|
||||
<span class="${class_name} toggle" style="${style}">
|
||||
<a class="option active ${css_class}" href="#"
|
||||
%if login_required and not c.user_is_loggedin:
|
||||
onclick="return showcover('', '${callback}_' + $(this).thing_id());"
|
||||
|
||||
@@ -46,7 +46,8 @@
|
||||
<input name="title" type="text" value="${thing.link.title if thing.link else ""}" />
|
||||
</td>
|
||||
<td>
|
||||
${error_field("NO_TITLE", "span")}
|
||||
${error_field("NO_TEXT", "title")}
|
||||
${error_field("TOO_LONG", "title")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -56,9 +57,9 @@
|
||||
value="${('self' if thing.link.is_self else thing.link.url) if thing.link else ""}"/>
|
||||
</td>
|
||||
<td>
|
||||
${error_field("NO_URL", "span")}
|
||||
${error_field("BAD_URL", "span")}
|
||||
${error_field("ALREADY_SUB", "span")}
|
||||
${error_field("NO_URL", "url")}
|
||||
${error_field("BAD_URL", "url")}
|
||||
${error_field("ALREADY_SUB", "url")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -78,7 +79,7 @@
|
||||
%endif
|
||||
</td>
|
||||
<td>
|
||||
${error_field("SUBREDDIT_NOEXIST")}
|
||||
${error_field("SUBREDDIT_NOEXIST", "sr")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -121,7 +122,7 @@
|
||||
</select>
|
||||
%endif
|
||||
</td>
|
||||
<td>${error_field("BAD_NUMBER", "span")}</td>
|
||||
<td>${error_field("BAD_NUMBER", "timelimitlength")}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
@@ -148,5 +149,5 @@
|
||||
>${text}</button>
|
||||
 
|
||||
<span class="status error"></span>
|
||||
${error_field("RATELIMIT", "span")}
|
||||
${error_field("RATELIMIT", "ratelimit")}
|
||||
</div>
|
||||
|
||||
@@ -97,4 +97,10 @@
|
||||
<div class="arrow upmod"></div>
|
||||
<div class="arrow down"></div>
|
||||
<div class="arrow downmod"></div>
|
||||
<div class="eb-sch"></div>
|
||||
<div class="eb-se"></div>
|
||||
<div class="eb-seh"></div>
|
||||
<div class="eb-vch"></div>
|
||||
<div class="eb-ve"></div>
|
||||
<div class="eb-veh"></div>
|
||||
</div>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
################################################################################
|
||||
|
||||
<%namespace file="utils.html" import="error_field, success_field"/>
|
||||
<%namespace name="utils" file="utils.html"/>
|
||||
|
||||
%if thing.done:
|
||||
<p class="error">your password has been reset and you've been logged in. Go use the site!</p>
|
||||
@@ -28,52 +29,27 @@
|
||||
${error_field("EXPIRED", 'p')}
|
||||
|
||||
<form id="chpass" method="post" action="/api/resetpassword"
|
||||
onsubmit="return post_form(this,'resetpassword')"
|
||||
class="pretty-form">
|
||||
<h1>reset your password</h1>
|
||||
onsubmit="return post_form(this,'resetpassword')">
|
||||
|
||||
<h1>choose a new password</h1>
|
||||
<input type="hidden" name="key" value="${thing.key}"/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
${_("username")}
|
||||
</td>
|
||||
<td>
|
||||
<input class="logtxt" name="name"
|
||||
id="name" type="text"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
${_("new password")}
|
||||
</td>
|
||||
<td>
|
||||
<input class="logtxt" name="passwd"
|
||||
id="passwd" type="password"/>
|
||||
</td>
|
||||
<td>
|
||||
${error_field("BAD_PASSWORD")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
${_("verify password")}
|
||||
</td>
|
||||
<td>
|
||||
<input class="logtxt" name="passwd2"
|
||||
id="passwd2" type="password"/>
|
||||
</td>
|
||||
<td>
|
||||
${error_field("BAD_PASSWORD_MATCH")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn" type="submit">submit</button>
|
||||
<span class="status"></span>
|
||||
</td><td></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('new password')}">
|
||||
<input type="password" name="passwd" />
|
||||
${error_field("BAD_PASSWORD", "passwd")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<div class="spacer">
|
||||
<%utils:round_field title="${_('verify password')}">
|
||||
<input type="password" name="passwd2" />
|
||||
${error_field("BAD_PASSWORD_MATCH", "passwd2")}
|
||||
</%utils:round_field>
|
||||
</div>
|
||||
|
||||
<button class="btn" type="submit">submit</button>
|
||||
<span class="status"></span>
|
||||
|
||||
</form>
|
||||
%endif
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
## 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
|
||||
## All portions of the code written by CondeNet are Copyright (c) 2006-2008
|
||||
## CondeNet, Inc. All Rights Reserved.
|
||||
################################################################################
|
||||
<%!
|
||||
from r2.lib.filters import unsafe, safemarkdown, keep_space
|
||||
%>
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
|
||||
|
||||
<div class="selftextcontainer">
|
||||
<div class="selftext rounded">
|
||||
${unsafe(safemarkdown(thing.link.selftext))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
class="pretty-form sharelink" action="/post/share"
|
||||
${"" if thing.link_name else "style='display:none'"}>
|
||||
<div class="clearleft"><!--IEsux--></div>
|
||||
${error_field("RATELIMIT")}
|
||||
${error_field("RATELIMIT", "ratelimit")}
|
||||
<input type="hidden" name="parent" value="${thing.link_name}"/>
|
||||
<table class="sharetable preftable">
|
||||
<tr class="">
|
||||
@@ -22,9 +22,9 @@
|
||||
</textarea>
|
||||
</td>
|
||||
<td class="share-to-errors">
|
||||
${error_field("BAD_EMAILS")}
|
||||
${error_field("TOO_MANY_EMAILS")}
|
||||
${error_field("NO_EMAILS")}
|
||||
${error_field("BAD_EMAILS", "share_to")}
|
||||
${error_field("TOO_MANY_EMAILS", "share_to")}
|
||||
${error_field("NO_EMAILS", "share_to")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -42,7 +42,7 @@
|
||||
name="share_from" />
|
||||
</td>
|
||||
<td class="share-from-errors">
|
||||
${error_field("COMMENT_TOO_LONG" )}
|
||||
${error_field("COMMENT_TOO_LONG", "share_from" )}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -60,8 +60,8 @@
|
||||
value="${c.user.email if hasattr(c.user, 'email') else ''}"/>
|
||||
</td>
|
||||
<td class="reply-to-errors">
|
||||
${error_field("BAD_EMAILS")}
|
||||
${error_field("TOO_MANY_EMAILS")}
|
||||
${error_field("BAD_EMAILS", "replyto")}
|
||||
${error_field("TOO_MANY_EMAILS", "replyto")}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -80,18 +80,16 @@
|
||||
</textarea>
|
||||
</td>
|
||||
<td class="message-errors">
|
||||
${error_field("COMMENT_TOO_LONG")}
|
||||
${error_field("TOO_LONG", "message")}
|
||||
</td>
|
||||
</tr>
|
||||
%if thing.captcha:
|
||||
${captchagen(thing.captcha.iden, thing.captcha.error,
|
||||
tabulate = False, size = 30)}
|
||||
${captchagen('', tabulate = False, size = 30)}
|
||||
%endif
|
||||
<tr>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
<span class="status error"></span>
|
||||
<button type="submit" class="btn">
|
||||
${_("share")}
|
||||
</button>
|
||||
@@ -99,6 +97,7 @@
|
||||
onclick="return cancelShare(this);">
|
||||
${_("cancel")}
|
||||
</button>
|
||||
<span class="status error"></span>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
</th>
|
||||
<td class="prefright">
|
||||
<input id="img-name" name="name" value="" type="text"/>
|
||||
${error_field("BAD_CSS_NAME", "span")}
|
||||
${error_field("BAD_CSS_NAME", "name")}
|
||||
<br/>
|
||||
<span class="little gray">
|
||||
${_("(image names should consist of alphanumeric characters and '-' only)")}
|
||||
|
||||
14
r2/r2/templates/tabbedpane.html
Normal file
@@ -0,0 +1,14 @@
|
||||
${thing.tabmenu.render()}
|
||||
<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()}
|
||||
</div>
|
||||
%endfor
|
||||
##select the front tab and init the other tabs
|
||||
<script type="text/javascript">
|
||||
select_tab("${thing.tabs[0][0]}", true)
|
||||
</script>
|
||||
|
||||
</div>
|
||||
@@ -34,7 +34,7 @@
|
||||
<input type="text" name="name" id="name"/>
|
||||
<button class="btn" type="submit">${_("add")}</button>
|
||||
<span class="status"></span>
|
||||
${error_field("USER_DOESNT_EXIST", "span")}
|
||||
${error_field("USER_DOESNT_EXIST", "name")}
|
||||
</form>
|
||||
%endif
|
||||
|
||||
|
||||
145
r2/r2/templates/usertext.html
Normal file
@@ -0,0 +1,145 @@
|
||||
## 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.
|
||||
################################################################################
|
||||
<%!
|
||||
from r2.lib.filters import unsafe, safemarkdown, keep_space
|
||||
from r2.lib.utils import randstr
|
||||
%>
|
||||
|
||||
<%namespace file="printable.html" import="toggle_button" />
|
||||
<%namespace file="utils.html" import="error_field"/>
|
||||
|
||||
<%def name="action_button(name, btn_type, onclick, display)">
|
||||
<button type="${btn_type}" onclick="${onclick}" class="${name}"
|
||||
${"style='display:none'" if not display else ""}>
|
||||
${name}
|
||||
</button>
|
||||
</%def>
|
||||
|
||||
%if thing.have_form:
|
||||
<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)}">
|
||||
%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 ''}"/>
|
||||
|
||||
%if not thing.creating:
|
||||
<div class="usertext-body">
|
||||
${unsafe(safemarkdown(thing.text, nofollow = thing.nofollow))}
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if thing.editable or thing.creating:
|
||||
##keep this on one line so we don't add extra spaces
|
||||
<div class="usertext-edit"
|
||||
style="${"" if thing.creating else 'display: none'}">
|
||||
##this div prevents IE7 from adding extra margin to the textarea.
|
||||
<div>
|
||||
<textarea rows="1" cols="1"
|
||||
name="text"
|
||||
>${keep_space(thing.text)}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="bottom-area">
|
||||
${toggle_button("help-toggle", _("formatting help"), _("hide help"),
|
||||
"helpon", "helpoff",
|
||||
style = "" if thing.creating else "display: none")}
|
||||
|
||||
${error_field("TOO_LONG", "text", "span")}
|
||||
${error_field("NO_TEXT", "text", "span")}
|
||||
|
||||
<div class="usertext-buttons">
|
||||
${action_button("save", "submit", "",
|
||||
thing.creating and thing.have_form)}
|
||||
${action_button("cancel", "button", "cancel_usertext(this)", False)}
|
||||
%if thing.have_form:
|
||||
<span class="status"></span>
|
||||
%endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class="markhelp md" style="display: none">
|
||||
<tr style="background-color: #ffff99; text-align: center">
|
||||
<td><em>${_( "you type:")}</em></td>
|
||||
<td><em>${_( "you see:")}</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>*${_( "italics")}*</td>
|
||||
<td><em>${_( "italics")}</em></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>**${_( "bold")}**</td>
|
||||
<td><b>${_( "bold")}</b></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[reddit!](http://reddit.com)</td>
|
||||
<td><a href="http://reddit.com">reddit!</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
* ${_( "item")} 1<br/>
|
||||
* ${_( "item")} 2<br/>
|
||||
* ${_( "item")} 3
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>${_( "item")} 1</li>
|
||||
<li>${_( "item")} 2</li>
|
||||
<li>${_( "item")} 3</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>> ${_( "quoted text")}</td>
|
||||
<td><blockquote>${_( "quoted text" )}</blockquote></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Here is some code:<br/>
|
||||
<span class="spaces">
|
||||
|
||||
</span>
|
||||
def foo():<br/>
|
||||
<span class="spaces">
|
||||
|
||||
</span>
|
||||
print "hello, world!"<br/>
|
||||
</td>
|
||||
<td>Here is come code:<br/>
|
||||
<pre>def foo():<br/> print "hello,
|
||||
world!"</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
%endif
|
||||
|
||||
%if thing.have_form:
|
||||
</form>
|
||||
%else:
|
||||
</div>
|
||||
%endif
|
||||
@@ -51,7 +51,7 @@ id="${arg}_${thing and thing._fullname or ''}"
|
||||
</%def>
|
||||
|
||||
<%def name="submit_form(onsubmit='', action='', _class='', method='post', _id='', **params)">
|
||||
<form class="pretty-form ${_class or ''}" onsubmit="${onsubmit or ''}"
|
||||
<form class="${_class or ''}" onsubmit="${onsubmit or ''}"
|
||||
action="${action or ''}" ${_id and "id='" + _id + "'" or ""} method="${method}"
|
||||
%if c.cname:
|
||||
target="_top"
|
||||
@@ -73,11 +73,11 @@ ${first_defined(kw[1:])}
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%def name="error_field(name, kind='p')">
|
||||
<${kind} class="error ${name}"
|
||||
style="${'' if name in c.errors else 'display:none'}">
|
||||
%if name in c.errors:
|
||||
${c.errors[name].message}
|
||||
<%def name="error_field(error_name, field_name, kind='span')">
|
||||
<${kind} class="error ${error_name} field-${field_name}"
|
||||
style="${'' if error_name in c.errors else 'display:none'}">
|
||||
%if error_name in c.errors:
|
||||
${c.errors[error_name].message}
|
||||
%endif
|
||||
</${kind}>
|
||||
</%def>
|
||||
@@ -393,3 +393,20 @@ ${unsafe(txt)}
|
||||
</a>
|
||||
</form>
|
||||
</%def>
|
||||
|
||||
<%def name="round_field(title, description = '', css_class= '', **kw)">
|
||||
<div class="roundfield ${css_class}"
|
||||
%for k, v in kw.iteritems():
|
||||
${k}="${v}"
|
||||
%endfor
|
||||
>
|
||||
<span class="title">${title}</span>
|
||||
 
|
||||
%if description:
|
||||
<span class="gray">${description}</span>
|
||||
%endif
|
||||
<div class="roundfield-content">
|
||||
${caller.body()}
|
||||
</div>
|
||||
</div>
|
||||
</%def>
|
||||
|
||||