Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

overhaul of JS and form handling code, not based on jQuery

  • Loading branch information...
commit 4440ccfc6e7d2329ccd349053138d2b6bf15a519 1 parent 7742fbf
@KeyserSosa KeyserSosa authored
Showing with 4,869 additions and 3,643 deletions.
  1. +6 −4 .gitignore
  2. +93 −0 r2/Makefile
  3. +0 −71 r2/compress_js.sh
  4. +2 −3 r2/r2/config/middleware.py
  5. +7 −2 r2/r2/config/routing.py
  6. +1 −0  r2/r2/config/templates.py
  7. +674 −760 r2/r2/controllers/api.py
  8. +16 −1 r2/r2/controllers/buttons.py
  9. +2 −0  r2/r2/controllers/error.py
  10. +12 −7 r2/r2/controllers/errors.py
  11. +47 −23 r2/r2/controllers/front.py
  12. +0 −1  r2/r2/controllers/i18n.py
  13. +8 −8 r2/r2/controllers/post.py
  14. +25 −40 r2/r2/controllers/reddit_base.py
  15. +141 −48 r2/r2/controllers/validator/validator.py
  16. +9 −4 r2/r2/lib/base.py
  17. +7 −9 r2/r2/lib/captcha.py
  18. +0 −476 r2/r2/lib/contrib/jsjam
  19. +218 −0 r2/r2/lib/contrib/jsmin.py
  20. +6 −9 r2/r2/lib/cssfilter.py
  21. +7 −3 r2/r2/lib/emailer.py
  22. +136 −128 r2/r2/lib/jsonresponse.py
  23. +67 −3 r2/r2/lib/jsontemplates.py
  24. +2 −2 r2/r2/lib/menus.py
  25. +21 −56 r2/r2/lib/pages/pages.py
  26. +0 −2  r2/r2/lib/strings.py
  27. +23 −3 r2/r2/lib/template_helpers.py
  28. +16 −0 r2/r2/lib/utils/utils.py
  29. +12 −1 r2/r2/models/account.py
  30. +18 −7 r2/r2/models/link.py
  31. +0 −1  r2/r2/models/listing.py
  32. +0 −28 r2/r2/models/report.py
  33. +38 −0 r2/r2/public/static/css/reddit-ie6-hax.css
  34. +133 −71 r2/r2/public/static/{ → css}/reddit.css
  35. 0  r2/r2/public/static/{ → defunct}/animate.js
  36. 0  r2/r2/public/static/{ → defunct}/comments.js
  37. 0  r2/r2/public/static/{ → defunct}/contacts.js
  38. 0  r2/r2/public/static/{ → defunct}/frame_piece.js
  39. 0  r2/r2/public/static/{ → defunct}/json.js
  40. 0  r2/r2/public/static/{ → defunct}/link.js
  41. 0  r2/r2/public/static/{ → defunct}/organic.js
  42. 0  r2/r2/public/static/{ → defunct}/psrs.js
  43. 0  r2/r2/public/static/{ → defunct}/reddit_piece.js
  44. 0  r2/r2/public/static/{ → defunct}/subreddit.js
  45. 0  r2/r2/public/static/{ → defunct}/tabular.js
  46. 0  r2/r2/public/static/{ → defunct}/utils.js
  47. 0  r2/r2/public/static/{ → defunct}/vote_piece.js
  48. +0 −117 r2/r2/public/static/inbound-email-policy.html
  49. +11 −0 r2/r2/public/static/js/jquery-1.2.6.js
  50. +1 −0  r2/r2/public/static/js/jquery.js
  51. +156 −0 r2/r2/public/static/js/jquery.json-1.3.js
  52. +1 −0  r2/r2/public/static/js/jquery.json.js
  53. +619 −0 r2/r2/public/static/js/jquery.reddit.js
  54. +568 −0 r2/r2/public/static/js/reddit.js
  55. +0 −297 r2/r2/public/static/json_large.js
  56. +0 −14 r2/r2/public/static/new_layout.html
  57. +1 −0  r2/r2/public/static/reddit-embed.css
  58. BIN  r2/r2/public/static/submit-hope.png
  59. +0 −8 r2/r2/public/static/sureroute.html
  60. +2 −2 r2/r2/templates/admin_rightbox.html
  61. +10 −9 r2/r2/templates/admintranslations.html
  62. +182 −0 r2/r2/templates/appservicemonitor.html
  63. +17 −0 r2/r2/templates/authorizedembed.html
  64. +4 −17 r2/r2/templates/base.html
  65. +12 −6 r2/r2/templates/base.htmllite
  66. +17 −15 r2/r2/templates/button.html
  67. +61 −54 r2/r2/templates/buttondemopanel.html
  68. +15 −15 r2/r2/templates/buttontypes.html
  69. +5 −6 r2/r2/templates/captcha.html
  70. +0 −13 r2/r2/templates/cnameframe.html
  71. +39 −18 r2/r2/templates/comment.html
  72. +12 −0 r2/r2/templates/comment.htmllite
  73. +12 −1 r2/r2/templates/comment.mobile
  74. +12 −2 r2/r2/templates/comment.xml
  75. +14 −11 r2/r2/templates/comment_skeleton.html
  76. +25 −35 r2/r2/templates/commentreplybox.html
  77. +48 −35 r2/r2/templates/createsubreddit.html
  78. +2 −1  r2/r2/templates/csserror.html
  79. +10 −17 r2/r2/templates/feedback.html
  80. +50 −0 r2/r2/templates/framebuster.html
  81. +50 −30 r2/r2/templates/frametoolbar.html
  82. +2 −31 r2/r2/templates/gettextheader.html
  83. +0 −55 r2/r2/templates/help.html
  84. +13 −0 r2/r2/templates/jquery.reddit.js
  85. +24 −43 r2/r2/templates/link.html
  86. +7 −32 r2/r2/templates/link.htmllite
  87. +1 −1  r2/r2/templates/linkcompressed.html
  88. +2 −2 r2/r2/templates/linkinfobar.html
  89. +2 −10 r2/r2/templates/linkpromoteinfobar.html
  90. +2 −4 r2/r2/templates/listing.html
  91. +2 −1  r2/r2/templates/listing.htmllite
  92. +35 −32 r2/r2/templates/login.html
  93. +11 −15 r2/r2/templates/loginformwide.html
  94. +6 −9 r2/r2/templates/message.html
  95. +4 −4 r2/r2/templates/messagecompose.html
  96. +3 −1 r2/r2/templates/morechildren.html
  97. +6 −1 r2/r2/templates/navmenu.html
  98. +11 −9 r2/r2/templates/newlink.html
  99. +64 −10 r2/r2/templates/organiclisting.html
  100. +1 −1  r2/r2/templates/panestack.html
  101. +1 −1  r2/r2/templates/password.html
  102. 0  r2/r2/templates/permalinkmessage.htmllite
  103. +2 −2 r2/r2/templates/prefdelete.html
  104. +3 −3 r2/r2/templates/prefupdate.html
  105. +128 −80 r2/r2/templates/printable.html
  106. +64 −0 r2/r2/templates/printable.htmllite
  107. +5 −16 r2/r2/templates/profilebar.html
  108. +3 −6 r2/r2/templates/promotedlink.html
  109. +4 −4 r2/r2/templates/promotedlinks.html
  110. +19 −22 r2/r2/templates/promotelinkform.html
  111. +14 −28 r2/r2/templates/reddit.html
  112. +29 −43 r2/r2/templates/redditfooter.html
  113. +13 −6 r2/r2/templates/redditheader.html
  114. +1 −1  r2/r2/templates/resetpassword.html
  115. +2 −2 r2/r2/templates/searchform.html
  116. +0 −112 r2/r2/templates/server_status_page.html
  117. +27 −30 r2/r2/templates/sharelink.html
  118. +7 −10 r2/r2/templates/subreddit.html
  119. +13 −32 r2/r2/templates/subredditinfobar.html
  120. +108 −104 r2/r2/templates/subredditstylesheet.html
  121. +10 −14 r2/r2/templates/translatedstring.html
  122. +26 −42 r2/r2/templates/translation.html
  123. +15 −15 r2/r2/templates/userlist.html
  124. +15 −11 r2/r2/templates/usertableitem.html
  125. +143 −32 r2/r2/templates/utils.html
  126. +8 −15 r2/r2/templates/widgetdemopanel.html
  127. +0 −34 r2/rtl.sh
  128. +310 −223 r2/supervise_watcher.py
View
10 .gitignore
@@ -15,11 +15,13 @@
lighttpd.**
development.ini
development_*.ini
+staging.ini
production.ini
-r2/r2/public/static/frame.js
-r2/r2/public/static/reddit.js
-r2/r2/public/static/vote.js
-r2/r2/public/static/reddit_rtl.css
+*.update
+r2/r2/public/static/*.js
+r2/r2/public/static/*.css
+r2/r2/**/defunct/**
+r2/r2/**/**/defunct/**
r2/r2admin
r2/reddit_i18n
r2/data/*
View
93 r2/Makefile
@@ -0,0 +1,93 @@
+# The contents of this file are subject to the Common Public Attribution
+# License Version 1.0. (the "License"); you may not use this file except in
+# compliance with the License. You may obtain a copy of the License at
+# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
+# License Version 1.1, but Sections 14 and 15 have been added to cover use of
+# software over a computer network and provide for limited attribution for the
+# Original Developer. In addition, Exhibit A has been modified to be consistent
+# with Exhibit B.
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
+# the specific language governing rights and limitations under the License.
+#
+# The Original Code is Reddit.
+#
+# The Original Developer is the Initial Developer. The Initial Developer of the
+# Original Code is CondeNet, Inc.
+#
+# All portions of the code written by CondeNet are Copyright (c) 2006-2008
+# CondeNet, Inc. All Rights Reserved.
+################################################################################
+
+# Jacascript files to be compressified
+js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js
+# CSS targets
+css_targets = reddit.css reddit-ie6-hax.css
+
+SED=sed
+
+package = r2
+static_dir = $(package)/public/static
+contrib = $(package)/lib/contrib
+
+JSCOMPRESS = $(contrib)/jsmin.py
+
+# If admin codebase is install, get its path so that we can build ini
+# files against the primary production.ini
+PRIVATEREPOS = $(shell python -c 'exec "try: import r2admin; print r2admin.__path__[0]\nexcept:pass"')
+
+#------
+
+JSTARGETS := $(foreach js, $(js_targets), $(static_dir)/$(js))
+CSSTARGETS := $(foreach css, $(css_targets), $(static_dir)/$(css))
+RTLCSS = $(CSSTARGETS:.css=-rtl.css)
+
+
+MD5S = $(JSTARGETS:=.md5) $(CSSTARGETS:=.md5)
+
+ifdef PRIVATEREPOS
+INIUPDATE = $(wildcard *.update)
+INIS = $(INIUPDATE:.update=.ini)
+
+%.ini: %.update
+ ln -sf `pwd`/$< $(PRIVATEREPOS)/..
+ make -C $(PRIVATEREPOS)/.. $@
+ ln -sf $(PRIVATEREPOS)/../$@ .
+endif
+
+all: $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(RTLCSS) $(INIS)
+
+.PHONY: js css md5 rtl clean all
+
+$(MD5S): %.md5 : %
+ cat $< | openssl md5 > $@
+
+$(JSTARGETS): $(static_dir)/%.js : $(static_dir)/js/%.js
+ $(JSCOMPRESS) < $< > $@
+
+$(CSSTARGETS): $(static_dir)/%.css : $(static_dir)/css/%.css
+ $(SED) -e 's/ \+/ /' \
+ -e 's/\/\*.*\*\///g' \
+ -e 's/: /:/' \
+ $< | grep -v "^ *$$" > $@
+
+$(RTLCSS): %-rtl.css : %.css
+ $(SED) -e "s/left/>####</g" \
+ -e "s/right/left/g" \
+ -e "s/>####</right/g" \
+ -e "s/\(margin\|padding\):\s*\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)/\1:\2 \5 \4 \3/g" $< > $@
+
+
+
+
+js: $(JSTARGETS)
+
+css: $(CSSTARGETS)
+
+md5: $(MD5S)
+
+rtl: $(RTLCSS)
+
+clean:
+ rm $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(INIS)
View
71 r2/compress_js.sh
@@ -1,71 +0,0 @@
-#!/bin/bash
-
-# The contents of this file are subject to the Common Public Attribution
-# License Version 1.0. (the "License"); you may not use this file except in
-# compliance with the License. You may obtain a copy of the License at
-# http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
-# License Version 1.1, but Sections 14 and 15 have been added to cover use of
-# software over a computer network and provide for limited attribution for the
-# Original Developer. In addition, Exhibit A has been modified to be consistent
-# with Exhibit B.
-#
-# Software distributed under the License is distributed on an "AS IS" basis,
-# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
-# the specific language governing rights and limitations under the License.
-#
-# The Original Code is Reddit.
-#
-# The Original Developer is the Initial Developer. The Initial Developer of the
-# Original Code is CondeNet, Inc.
-#
-# All portions of the code written by CondeNet are Copyright (c) 2006-2008
-# CondeNet, Inc. All Rights Reserved.
-################################################################################
-
-files=( psrs.js utils.js animate.js link.js comments.js subreddit.js vote_piece.js reddit_piece.js organic.js )
-
-wd=`pwd`
-redditjs='reddit.js'
-framejs='frame.js'
-votejs='vote.js'
-compressor=" $wd/r2/lib/contrib/jsjam -g -i"
-
-
-echo "generating rtl style sheet"
-
-./rtl.sh
-
-
-echo "Generating reddit.js..."
-
-cd r2/public/static
-[ -e $redditjs ] && rm $redditjs
-[ -e $redditjs-big ] && rm $redditjs-big
-
-cat json.js > $redditjs.tmp
-for f in "${files[@]}"
-do
- $compressor $f >> $redditjs.tmp
-done;
-sed 's/\$/ \$/g' $redditjs.tmp > $redditjs
-
-
-echo "Generating vote.js..."
-# compress the votes alone (for buttons)
-cat psrs.js | $compressor | sed 's/\$/ \$/g' > $votejs
-cat utils.js vote_piece.js | $compressor >> $votejs
-
-echo "Generating frame.js..."
-# compress frame alone (for the toolbar)
-cat psrs.js > $framejs
-cat vote_piece.js utils.js frame_piece.js | $compressor >> $framejs
-
-echo "droppping md5s..."
-for file in *.js
-do
- cat $file | openssl md5 > $file.md5
-done
-for file in *.css
-do
- cat $file | openssl md5 > $file.md5
-done
View
5 r2/r2/config/middleware.py
@@ -200,7 +200,6 @@ def __call__(self, environ, start_response):
#this is a cname, and redirect to the frame controller.
#Ignore localhost so paster shell still works.
#If this is an error, don't redirect
-
if (not sub_domains.endswith(base_domain)
and (not sub_domains == 'localhost')):
environ['sub_domain'] = sub_domains
@@ -210,11 +209,11 @@ def __call__(self, environ, start_response):
elif self.is_auth_cname(sub_domains):
environ['frameless_cname'] = True
environ['authorized_cname'] = True
- elif ("redditSession" in environ.get('HTTP_COOKIE', '')
+ elif ("redditSession=cname" in environ.get('HTTP_COOKIE', '')
and environ['REQUEST_METHOD'] != 'POST'
and not environ['PATH_INFO'].startswith('/error')):
environ['original_path'] = environ['PATH_INFO']
- environ['PATH_INFO'] = '/frame'
+ environ['FULLPATH'] = environ['PATH_INFO'] = '/frame'
else:
environ['frameless_cname'] = True
return self.app(environ, start_response)
View
9 r2/r2/config/routing.py
@@ -72,11 +72,12 @@ def make_map(global_conf={}, app_conf={}):
mc('/feedback', controller='feedback', action='feedback')
mc('/ad_inq', controller='feedback', action='ad_inq')
- mc('/admin/:action', controller='admin')
mc('/admin/i18n', controller='i18n', action='list')
mc('/admin/i18n/:action', controller='i18n')
mc('/admin/i18n/:action/:lang', controller='i18n')
+
+ mc('/admin/:action', controller='admin')
mc('/user/:username/:where', controller='user', action='listing',
where='overview')
@@ -100,6 +101,9 @@ def make_map(global_conf={}, app_conf={}):
mc('/mail/optin', controller='front', action = 'optin')
mc('/stylesheet', controller = 'front', action = 'stylesheet')
mc('/frame', controller='front', action = 'frame')
+ mc('/framebuster/:blah', controller='front', action = 'framebuster')
+ mc('/framebuster/:what/:blah',
+ controller='front', action = 'framebuster')
mc('/promote/edit_promo/:link', controller='promote', action = 'edit_promo')
mc('/promote/:action', controller='promote')
@@ -152,6 +156,7 @@ def make_map(global_conf={}, app_conf={}):
mc('/mobile', controller='redirect', action='redirect',
dest='http://m.reddit.com/')
+ mc('/authorize_embed', controller = 'front', action = 'authorize_embed')
# This route handles displaying the error page and
# graphics used in the 404/500
@@ -161,6 +166,6 @@ def make_map(global_conf={}, app_conf={}):
mc('/error/document/:id', controller='error', action="document")
mc("/*url", controller='front', action='catchall')
-
+
return map
View
1  r2/r2/config/templates.py
@@ -41,5 +41,6 @@ def api(type, cls):
api('reddit', RedditJsonTemplate)
api('panestack', PanestackJsonTemplate)
api('listing', ListingJsonTemplate)
+api('usertableitem', UserItemJsonTemplate)
api('organiclisting', OrganicListingJsonTemplate)
View
1,434 r2/r2/controllers/api.py
@@ -39,17 +39,13 @@
BannedList, BoringPage, FormPage, NewLink, CssError, UploadedImage
from r2.lib.menus import CommentSortMenu
-from r2.lib.translation import Translator
from r2.lib.normalized_hot import expire_hot
from r2.lib.captcha import get_iden
from r2.lib import emailer
from r2.lib.strings import strings
from r2.lib.memoize import clear_memo
-from r2.lib.filters import _force_unicode
+from r2.lib.filters import _force_unicode, websafe_json
from r2.lib.db import queries
-from r2.config import cache
-from r2.lib.jsonresponse import JsonResponse, Json
-from r2.lib.jsontemplates import api_type
from r2.lib import cssfilter
from r2.lib import tracking
from r2.lib.media import force_thumbnail, thumbnail_url
@@ -63,6 +59,11 @@
from r2.lib.promote import promote, unpromote, get_promoted
def link_listing_by_url(url, count = None):
+ """
+ Generates a listing of links which share a common url, filtered by
+ the subreddit the current user is subscribed to (or the default
+ list of the user is not logged in.)
+ """
try:
links = list(tup(Link._by_url(url, sr = c.site)))
links.sort(key = lambda x: -x._score)
@@ -71,166 +72,115 @@ def link_listing_by_url(url, count = None):
except NotFound:
links = ()
- names = [l._fullname for l in links]
+ user = c.user if c.user_is_loggedin else None
+ names = [l._fullname for l in links
+ if l.sr_id in Subreddit.user_subreddits(user, limit = None)]
+
builder = IDBuilder(names, num = 25)
- listing = LinkListing(builder).listing()
- return listing
+ return LinkListing(builder).listing()
+
class ApiController(RedditController):
- def response_func(self, **kw):
- return self.sendstring(dumps(kw))
+ """
+ Controller which deals with almost all AJAX site interaction.
+ """
- @Json
- def ajax_login_redirect(self, res, dest):
- res._redirect("/login" + query_string(dict(dest=dest)))
+ def response_func(self, **kw):
+ data = dumps(kw)
+ if request.method == "GET" and request.GET.get("callback"):
+ return "%s(%s)" % (websafe_json(request.GET.get("callback")),
+ websafe_json(data))
+ return self.sendstring(data)
- def link_exists(self, url, sr, message = False):
- try:
- l = Link._by_url(url, sr)
- if message:
- return l.already_submitted_link()
- else:
- return l.make_permalink_slow()
- except NotFound:
- pass
+ @validatedForm()
+ def ajax_login_redirect(self, form, jquery, dest):
+ jquery.redirect("/login" + query_string(dict(dest=dest)))
- @validate(url = nop("url"),
- sr = VSubredditName,
+ @validate(link = VUrl(['url']),
count = VLimit('limit'))
- def GET_info(self, url, sr, count):
- listing = link_listing_by_url(url, count = count)
- res = BoringPage(_("API"),
- content = listing).render()
- return res
-
- @Json
- @validate(VCaptcha(),
- name=VRequired('name', errors.NO_NAME),
- email=VRequired('email', errors.NO_EMAIL),
- replyto = nop('replyto'),
- reason = nop('reason'),
- message=VRequired('message', errors.NO_MESSAGE))
- def POST_feedback(self, res, name, email, replyto, reason, message):
- res._update('status', innerHTML = '')
- if res._chk_error(errors.NO_NAME):
- res._focus("name")
- elif res._chk_error(errors.NO_EMAIL):
- res._focus("email")
- elif res._chk_error(errors.NO_MESSAGE):
- res._focus("personal")
- elif res._chk_captcha(errors.BAD_CAPTCHA):
- pass
-
- if not res.error:
+ def GET_info(self, link, count):
+ """
+ Get's a listing of links which have the provided url.
+ """
+ listing = None
+ if link and errors.ALREADY_SUB in c.errors:
+ listing = link_listing_by_url(request.params.get('url'),
+ count = count)
+ return BoringPage(_("API"), content = listing).render()
+
+ @validatedForm(VCaptcha(),
+ name=VRequired('name', errors.NO_NAME),
+ email=VRequired('email', errors.NO_EMAIL),
+ replyto = ValidEmails("replyto", num = 1),
+ reason = VOneOf('reason', ('ad_inq', 'feedback')),
+ message=VRequired('message', errors.NO_MESSAGE),
+ )
+ def POST_feedback(self, form, jquery, name, email,
+ replyto, 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)):
if reason != 'ad_inq':
emailer.feedback_email(email, message, name = name or '',
reply_to = replyto or '')
else:
emailer.ad_inq_email(email, message, name = name or '',
reply_to = replyto or '')
- res._update('success',
- innerHTML=_("thanks for your message! you should hear back from us shortly."))
- res._update("personal", value='')
- res._update("captcha", value='')
- res._hide("wtf")
+ form.set_html(".success", _("thanks for your message! "
+ "you should hear back from us shortly."))
+ form.set_inputs(personal = "", captcha = "")
+
POST_ad_inq = POST_feedback
- @Json
- @validate(VCaptcha(),
- VUser(),
- VModhash(),
- ip = ValidIP(),
- to = VExistingUname('to'),
- subject = VRequired('subject', errors.NO_SUBJECT),
- body = VMessage('message'))
- def POST_compose(self, res, to, subject, body, ip):
- res._update('status', innerHTML='')
- if (res._chk_error(errors.NO_USER) or
- res._chk_error(errors.USER_DOESNT_EXIST)):
- res._focus('to')
- elif res._chk_error(errors.NO_SUBJECT):
- res._focus('subject')
- elif (res._chk_error(errors.NO_MSG_BODY) or
- res._chk_error(errors.COMMENT_TOO_LONG)):
- res._focus('message')
- elif res._chk_captcha(errors.BAD_CAPTCHA):
- pass
- if not res.error:
+ @validatedForm(VCaptcha(),
+ VUser(),
+ VModhash(),
+ ip = ValidIP(),
+ to = VExistingUname('to'),
+ subject = VRequired('subject', errors.NO_SUBJECT),
+ body = VMessage('message'))
+ def POST_compose(self, form, jquery, to, subject, body, ip):
+ """
+ handles message composition under /message/compose.
+ """
+ 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) or
+ form.chk_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)
- res._update('success',
- innerHTML=_("your message has been delivered"))
- res._update('to', value='')
- res._update('subject', value='')
- res._update('message', value='')
+ form.set_html(".success", _("your message has been delivered"))
+ form.set_inputs(to = "", subject = "", message = "")
if g.write_query_queue:
queries.new_message(m, inbox_rel)
- else:
- res._update('success', innerHTML='')
-
-
- @validate(VUser(),
- VSRSubmitPage(),
- url = VRequired('url', None),
- title = VRequired('title', None))
- def GET_submit(self, url, title):
- if url and not request.get.get('resubmit'):
- listing = link_listing_by_url(url)
- redirect_link = None
- if listing.things:
- if len(listing.things) == 1:
- redirect_link = listing.things[0]
- else:
- subscribed = [l for l in listing.things
- if c.user_is_loggedin
- and l.subreddit.is_subscriber_defaults(c.user)]
-
- #if there is only 1 link to be displayed, just go there
- if len(subscribed) == 1:
- redirect_link = subscribed[0]
- else:
- infotext = strings.multiple_submitted % \
- listing.things[0].resubmit_link()
- res = BoringPage(_("seen it"),
- content = listing,
- infotext = infotext).render()
- return res
-
- if redirect_link:
- return self.redirect(redirect_link.already_submitted_link)
-
- captcha = Captcha() if c.user.needs_captcha() else None
- sr_names = Subreddit.submit_sr_names(c.user) if c.default_sr else ()
- return FormPage(_("submit"),
- content=NewLink(url=url or '',
- title=title or '',
- subreddits = sr_names,
- captcha=captcha)).render()
- @Json
- @validate(VUser(),
- VCaptcha(),
- ValidDomain('url'),
- VRatelimit(rate_user = True, rate_ip = True, prefix='rate_submit_'),
- ip = ValidIP(),
- sr = VSubmitSR('sr'),
- url = VUrl(['url', 'sr']),
- title = VTitle('title'),
- save = nop('save'),
- )
- def POST_submit(self, res, url, title, save, sr, ip):
- res._update('status', innerHTML = '')
- if isinstance(url, str):
- res._update('url', value=url)
+ @validatedForm(VUser(),
+ VCaptcha(),
+ ValidDomain('url'),
+ VRatelimit(rate_user = True, rate_ip = True,
+ prefix = "rate_submit_"),
+ ip = ValidIP(),
+ sr = VSubmitSR('sr'),
+ url = VUrl(['url', 'sr']),
+ title = VTitle('title'),
+ save = VBoolean('save'),
+ )
+ def POST_submit(self, form, jquery, url, title, save, sr, ip):
+ if isinstance(url, (unicode, str)):
+ form.set_inputs(url = url)
should_ratelimit = sr.should_ratelimit(c.user, 'link')
@@ -239,33 +189,26 @@ def POST_submit(self, res, url, title, save, sr, ip):
c.errors.remove(errors.RATELIMIT)
# check for no url, or clear that error field on return
- if res._chk_errors((errors.NO_URL, errors.BAD_URL)):
- res._focus('url')
- elif res._chk_error(errors.ALREADY_SUB):
- link = url[0]
- res._redirect(link.already_submitted_link)
- #ratelimiter
- elif res._chk_error(errors.RATELIMIT):
+ if form.has_errors("url", errors.NO_URL, errors.BAD_URL):
pass
+ elif form.has_errors("url", errors.ALREADY_SUB):
+ jquery.redirect(url[0].already_submitted_link)
# check for title, otherwise look it up and return it
- elif res._chk_error(errors.NO_TITLE):
- # clear out this error
- res._chk_error(errors.TITLE_TOO_LONG)
+ elif form.has_errors("title", errors.NO_TITLE):
# try to fetch the title
title = get_title(url)
if title:
- res._update('title', value = title)
- res._focus('title')
- res._clear_error(errors.NO_TITLE)
- c.errors.remove(errors.NO_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
- res._focus('title')
- elif res._chk_error(errors.TITLE_TOO_LONG):
- res._focus('title')
- elif res._chk_captcha(errors.BAD_CAPTCHA):
+
+ elif (form.has_errors("title", errors.TITLE_TOO_LONG) or
+ form.chk_captcha(errors.BAD_CAPTCHA, errors.RATELIMIT)):
pass
- if res.error or not title: return
+ if form.has_error() or not title: return
# check whether this is spam:
spam = (c.user._spam or
@@ -280,13 +223,19 @@ def POST_submit(self, res, url, title, save, sr, ip):
l._commit()
l.set_url_cache()
v = Vote.vote(c.user, l, True, ip, spam)
- if save == 'on':
+ if save:
r = l._save(c.user)
if g.write_query_queue:
queries.new_savehide(r)
+
+ #reset the hot page
+ if v.valid_thing:
+ expire_hot(sr)
+
#set the ratelimiter
if should_ratelimit:
- VRatelimit.ratelimit(rate_user=True, rate_ip = True, prefix='rate_submit_')
+ VRatelimit.ratelimit(rate_user=True, rate_ip = True,
+ prefix = "rate_submit_")
#update the queries
if g.write_query_queue:
@@ -307,216 +256,240 @@ def POST_submit(self, res, url, title, save, sr, ip):
c.cname = False
path = l.make_permalink_slow()
c.cname = cname
+ jquery.redirect(path)
- res._redirect(path)
-
-
- def _login(self, res, user, dest='', rem = None):
+ def _login(self, jquery, user, dest='', rem = None):
+ """
+ AJAX login handler, used by both login and register to set the
+ user cookie and send back a redirect.
+ """
self.login(user, rem = rem)
dest = dest or request.referer or '/'
- res._redirect(dest)
-
- @Json
- @validate(user = VLogin(['user_login', 'passwd_login']),
- op = VOneOf('op', options = ("login-main", "reg", "login"),
- default = 'login'),
- dest = nop('dest'),
- rem = nop('rem'),
- reason = VReason('reason'))
- def POST_login(self, res, user, op, dest, rem, reason):
- if reason and reason[0] == 'redirect':
- dest = reason[1]
-
- res._update('status_' + op, innerHTML='')
- if res._chk_error(errors.WRONG_PASSWORD, op):
- res._focus('passwd_' + op)
- else:
- self._login(res, user, dest, rem == 'on')
-
-
- @Json
- @validate(VCaptcha(),
- VRatelimit(rate_ip = True, prefix='rate_register_'),
- name = VUname(['user_reg']),
- email = nop('email_reg'),
- password = VPassword(['passwd_reg', 'passwd2_reg']),
- op = VOneOf('op', options = ("login-main", "reg", "login"),
- default = 'login'),
- dest = nop('dest'),
- rem = nop('rem'),
- reason = VReason('reason'))
- def POST_register(self, res, name, email, password, op, dest, rem, reason):
- res._update('status_' + op, innerHTML='')
- if res._chk_error(errors.BAD_USERNAME, op):
- res._focus('user_reg')
- elif res._chk_error(errors.USERNAME_TAKEN, op):
- res._focus('user_reg')
- elif res._chk_error(errors.BAD_PASSWORD, op):
- res._focus('passwd_reg')
- elif res._chk_error(errors.BAD_PASSWORD_MATCH, op):
- res._focus('passwd2_reg')
- elif res._chk_error(errors.DRACONIAN, op):
- res._focus('legal_reg')
- elif res._chk_captcha(errors.BAD_CAPTCHA):
- pass
- elif res._chk_error(errors.RATELIMIT, op):
- pass
+ jquery.redirect(dest)
- if res.error:
- return
- user = register(name, password)
- VRatelimit.ratelimit(rate_ip = True, prefix='rate_register_')
+ @validatedForm(user = VLogin(['user', 'passwd']),
+ dest = nop('dest'),
+ rem = VBoolean('rem'),
+ reason = VReason('reason'))
+ def POST_login(self, form, jquery, user, dest, rem, reason):
+ if reason and reason[0] == 'redirect':
+ dest = reason[1]
+ if not form.has_errors("passwd", errors.WRONG_PASSWORD):
+ self._login(jquery, user, dest, rem)
+
+
+ @validatedForm(VCaptcha(),
+ VRatelimit(rate_ip = True, prefix = "rate_register_"),
+ name = VUname(['user']),
+ email = ValidEmails("email", num = 1),
+ password = VPassword(['passwd', 'passwd2']),
+ dest = nop('dest'),
+ rem = VBoolean('rem'),
+ reason = VReason('reason'))
+ def POST_register(self, form, jquery, name, email,
+ password, dest, rem, reason):
+ if not (form.has_errors("name", errors.BAD_USERNAME,
+ errors.USERNAME_TAKEN) or
+ 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)):
+ user = register(name, password)
+ VRatelimit.ratelimit(rate_ip = True, prefix = "rate_register_")
+
+ #anything else we know (email, languages)?
+ if email:
+ user.email = email
+
+ user.pref_lang = c.lang
+ if c.content_langs == 'all':
+ user.pref_content_langs = 'all'
+ else:
+ langs = list(c.content_langs)
+ langs.sort()
+ user.pref_content_langs = tuple(langs)
+
+ d = c.user._dirties.copy()
+ user._commit()
+
+ c.user = user
+ if reason:
+ if reason[0] == 'redirect':
+ dest = reason[1]
+ elif reason[0] == 'subscribe':
+ for sr, sub in reason[1].iteritems():
+ self._subscribe(sr, sub)
+
+ self._login(jquery, user, dest, rem)
+
- #anything else we know (email, languages)?
- if email:
- user.email = email
+ @noresponse(VUser(),
+ VModhash(),
+ container = VByName('id'))
+ def POST_leave_moderator(self, container):
+ """
+ Handles self-removal as moderator from a subreddit as rendered
+ in the subreddit sidebox on any of that subreddit's pages.
+ """
+ if container and container.is_moderator(c.user):
+ container.remove_moderator(c.user)
- user.pref_lang = c.lang
- if c.content_langs == 'all':
- user.pref_content_langs = 'all'
- else:
- langs = list(c.content_langs)
- langs.sort()
- user.pref_content_langs = tuple(langs)
+ @noresponse(VUser(),
+ VModhash(),
+ container = VByName('id'))
+ def POST_leave_contributor(self, container):
+ """
+ same comment as for POST_leave_moderator.
+ """
+ if container and container.is_contributor(c.user):
+ container.remove_contributor(c.user)
- d = c.user._dirties.copy()
- user._commit()
-
- c.user = user
- if reason:
- if reason[0] == 'redirect':
- dest = reason[1]
- elif reason[0] == 'subscribe':
- for sr, sub in reason[1].iteritems():
- self._subscribe(sr, sub)
-
- self._login(res, user, dest, rem)
+ @noresponse(VUser(),
+ VModhash(),
+ nuser = VExistingUname('name'),
+ iuser = VByName('id'),
+ container = VByName('container'),
+ type = VOneOf('type', ('friend', 'moderator',
+ 'contributor', 'banned')))
+ def POST_unfriend(self, nuser, iuser, container, type):
+ """
+ Handles removal of a friend (a user-user relation) or removal
+ of a user's priviledges from a subreddit (a user-subreddit
+ relation). The user can either be passed in by name (nuser)
+ or buy fullname (iuser). 'container' will either be the
+ current user or the subreddit.
- @Json
- @validate(VUser(),
- VModhash(),
- container = VByName('id'),
- type = VOneOf('location', ('moderator', 'contributor')))
- def POST_leave(self, res, container, type):
- if container and c.user:
- res._hide("pre_" + container._fullname)
- res._hide("thingrow_" + container._fullname)
- fn = getattr(container, 'remove_' + type)
- fn(c.user)
-
- @Json
- @validate(VUser(),
- VModhash(),
- ip = ValidIP(),
- action = VOneOf('action', ('add', 'remove')),
- redirect = nop('redirect'),
- friend = VExistingUname('name'),
- container = VByName('container'),
- type = VOneOf('type', ('friend', 'moderator', 'contributor', 'banned')))
- def POST_friend(self, res, ip, friend, action, redirect, container, type):
- res._update('status', innerHTML='')
-
- fn = getattr(container, action + '_' + type)
+ """
+ # The user who made the request must be an admin or a moderator
+ # for the privilege change to succeed.
+ if (not c.user_is_admin
+ and (type in ('moderator','contributer','banned')
+ and not c.site.is_moderator(c.user))):
+ abort(403, 'forbidden')
+ # if we are (strictly) unfriending, the container had better
+ # be the current user.
+ if type == "friend" and container != c.user:
+ abort(403, 'forbidden')
+ fn = getattr(container, 'remove_' + type)
+ fn(iuser or nuser)
+
+
+
+ @validatedForm(VUser(),
+ VModhash(),
+ ip = ValidIP(),
+ friend = VExistingUname('name'),
+ container = VByName('container'),
+ type = VOneOf('type', ('friend', 'moderator',
+ 'contributor', 'banned')))
+ def POST_friend(self, form, jquery, ip, friend,
+ container, type):
+ """
+ Complement to POST_unfriend: handles friending as well as
+ privilege changes on subreddits.
+ """
+ fn = getattr(container, 'add_' + type)
+ # The user who made the request must be an admin or a moderator
+ # for the privilege change to succeed.
if (not c.user_is_admin
and (type in ('moderator','contributer','banned')
and not c.site.is_moderator(c.user))):
+ abort(403,'forbidden')
+ # if we are (strictly) friending, the container had better
+ # be the current user.
+ if type == "friend" and container != c.user:
abort(403,'forbidden')
- elif action == 'add':
- if res._chk_errors((errors.USER_DOESNT_EXIST,
- errors.NO_USER)):
- res._focus('name')
- else:
- new = fn(friend)
- cls = dict(friend=FriendList,
- moderator=ModList,
- contributor=ContributorList,
- banned=BannedList).get(type)
- res._update('name', value = '')
+
+ elif not form.has_errors("name",
+ errors.USER_DOESNT_EXIST, errors.NO_USER):
+ new = fn(friend)
+ cls = dict(friend=FriendList,
+ moderator=ModList,
+ contributor=ContributorList,
+ banned=BannedList).get(type)
+ form.set_inputs(name = "")
+ form.set_html(".status:first", _("added"))
+ if new and cls:
+ user_row = cls().user_row(friend)
+ jquery("table").insert_table_rows(user_row)
- #subscribing doesn't need a response
- if new and cls:
- res.object = cls().ajax_user(friend).for_ajax('add')
-
- if type != 'friend':
- msg = strings.msg_add_friend.get(type)
- subj = strings.subj_add_friend.get(type)
- if msg and subj and friend.name != c.user.name:
- # fullpath with domain needed or the markdown link
- # will break
- d = dict(url = container.path,
- title = container.title)
- msg = msg % d
- subj = subj % d
- Message._new(c.user, friend, subj, msg, ip,
- c.user._spam)
- elif action == 'remove' and friend:
- fn(friend)
-
-
- @Json
- @validate(VUser('curpass', default = ''),
- VModhash(),
- curpass = nop('curpass'),
- email = ValidEmails("email", num = 1),
- newpass = nop("newpass"),
- verpass = nop("verpass"),
- password = VPassword(['newpass', 'verpass']))
- def POST_update(self, res, email, curpass, password, newpass, verpass):
- res._update('status', innerHTML='')
- if res._chk_error(errors.WRONG_PASSWORD):
- res._focus('curpass')
- res._update('curpass', value='')
- return
+ if type != 'friend':
+ msg = strings.msg_add_friend.get(type)
+ subj = strings.subj_add_friend.get(type)
+ if msg and subj and friend.name != c.user.name:
+ # fullpath with domain needed or the markdown link
+ # will break
+ d = dict(url = container.path,
+ title = container.title)
+ msg = msg % d
+ subj = subj % d
+ Message._new(c.user, friend, subj, msg, ip,
+ c.user._spam)
+
+
+ @validatedForm(VUser('curpass', default = ''),
+ VModhash(),
+ email = ValidEmails("email", num = 1),
+ password = VPassword(['newpass', 'verpass']))
+ def POST_update(self, form, jquery, email, password):
+ """
+ handles /prefs/update for updating email address and password.
+ """
+ # 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
+ # different from the current address (or there is not one
+ # currently) apply it
updated = False
- if res._chk_error(errors.BAD_EMAILS):
- res._focus('email')
- elif email and (not hasattr(c.user,'email')
- or c.user.email != email):
+ if (not form.has_errors("email", errors.BAD_EMAILS) and
+ email and (not hasattr(c.user,'email') or c.user.email != email)):
c.user.email = email
c.user._commit()
- res._update('status',
- innerHTML=_('your email has been updated'))
+ form.set_html('.status', _('your email has been updated'))
updated = True
- if newpass or verpass:
- if res._chk_error(errors.BAD_PASSWORD):
- res._focus('newpass')
- elif res._chk_error(errors.BAD_PASSWORD_MATCH):
- res._focus('verpass')
- res._update('verpass', value='')
+ # change password
+ 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)
+ if updated:
+ form.set_html(".status",
+ _('your email and password have been updated'))
else:
- change_password(c.user, password)
- if updated:
- res._update('status',
- innerHTML=_('your email and password have been updated'))
- else:
- res._update('status',
- innerHTML=_('your password has been updated'))
- self.login(c.user)
-
- @Json
- @validate(VUser(),
- VModhash(),
- areyousure1 = nop('areyousure1'),
- areyousure2 = nop('areyousure2'),
- areyousure3 = nop('areyousure3'))
- def POST_delete_user(self, res, areyousure1, areyousure2, areyousure3):
+ form.set_html('.status',
+ _('your password has been updated'))
+ # the password has changed, so the user's cookie has been
+ # invalidated. drop a new cookie.
+ self.login(c.user)
+
+ @validatedForm(VUser(),
+ VModhash(),
+ areyousure1 = VOneOf('areyousure1', ('yes', 'no')),
+ areyousure2 = VOneOf('areyousure2', ('yes', 'no')),
+ areyousure3 = VOneOf('areyousure3', ('yes', 'no')))
+ def POST_delete_user(self, form, jquery,
+ areyousure1, areyousure2, areyousure3):
+ """
+ /prefs/delete. Make sure there are three yes's.
+ """
if areyousure1 == areyousure2 == areyousure3 == 'yes':
c.user.delete()
- res._redirect('/?deleted=true')
+ jquery.redirect('/?deleted=true')
else:
- res._update('status',
- innerHTML = _("see? you don't really want to leave"))
+ form.set_html('.status', _("see? you don't really want to leave"))
- @Json
- @validate(VUser(),
- VModhash(),
- thing = VByNameIfAuthor('id'))
- def POST_del(self, res, thing):
+ @noresponse(VUser(),
+ VModhash(),
+ thing = VByNameIfAuthor('id'))
+ def POST_del(self, thing):
'''for deleting all sorts of things'''
thing._deleted = True
thing._commit()
@@ -538,45 +511,42 @@ def POST_del(self, res, thing):
if g.use_query_cache:
queries.new_comment(thing, None)
- @Json
- @validate(VUser(), VModhash(),
- thing = VByName('id'))
- def POST_report(self, res, thing):
+ @noresponse(VUser(), VModhash(),
+ thing = VByName('id'))
+ def POST_report(self, thing):
'''for reporting...'''
- Report.new(c.user, thing)
+ if (thing and not thing._deleted and
+ not (hasattr(thing, "promoted") and thing.promoted)):
+ Report.new(c.user, thing)
- @Json
- @validate(VUser(), VModhash(),
- comment = VByNameIfAuthor('id'),
+ @validatedForm(VUser(), VModhash(),
+ comment = VByNameIfAuthor('parent'),
body = VComment('comment'))
- def POST_editcomment(self, res, comment, body):
- res._update('status_' + comment._fullname, innerHTML = '')
+ def POST_editcomment(self, form, jquery, comment, body):
- if not res._chk_errors((errors.BAD_COMMENT,errors.COMMENT_TOO_LONG,errors.NOT_AUTHOR),
- comment._fullname):
+ if not form.has_errors("comment",
+ errors.BAD_COMMENT, errors.COMMENT_TOO_LONG,
+ errors.NOT_AUTHOR):
comment.body = body
if not c.user_is_admin: comment.editted = True
comment._commit()
- res._send_things(comment)
+
+ jquery.replace_things(comment, True, True)
# flag search indexer that something has changed
tc.changed(comment)
- @Json
- @validate(VUser(),
+ @validatedForm(VUser(),
VModhash(),
- VRatelimit(rate_user = True, rate_ip = True, prefix = "rate_comment_"),
+ VRatelimit(rate_user = True, rate_ip = True,
+ prefix = "rate_comment_"),
ip = ValidIP(),
- parent = VSubmitParent('id'),
+ parent = VSubmitParent('parent'),
comment = VComment('comment'))
- def POST_comment(self, res, parent, comment, ip):
-
- #wipe out the status message
- res._update('status_' + parent._fullname, innerHTML = '')
-
+ def POST_comment(self, commentform, jquery, parent, comment, ip):
should_ratelimit = True
#check the parent type here cause we need that for the
#ratelimit checks
@@ -600,68 +570,77 @@ def POST_comment(self, res, parent, comment, ip):
if not should_ratelimit:
c.errors.remove(errors.RATELIMIT)
- if res._chk_errors((errors.BAD_COMMENT,errors.COMMENT_TOO_LONG, errors.RATELIMIT),
- parent._fullname):
- res._focus("comment_reply_" + parent._fullname)
- return
- res._show('reply_' + parent._fullname)
- res._update("comment_reply_" + parent._fullname, rows = 2)
-
- spam = (c.user._spam or
- errors.BANNED_IP in c.errors)
-
- if is_message:
- to = Account._byID(parent.author_id)
- subject = parent.subject
- re = "re: "
- if not subject.startswith(re):
- subject = re + subject
- item, inbox_rel = Message._new(c.user, to, subject, comment, ip, spam)
- item.parent_id = parent._id
- res._send_things(item)
- else:
- item, inbox_rel = Comment._new(c.user, link, parent_comment, comment,
- ip, spam)
- Vote.vote(c.user, item, True, ip)
- res._update("comment_reply_" + parent._fullname,
- innerHTML='', value='')
- res._send_things(item)
- res._hide('noresults')
- # flag search indexer that something has changed
- tc.changed(item)
-
- #update last modified
- set_last_modified(c.user, 'overview')
- set_last_modified(c.user, 'commented')
- set_last_modified(link, 'comments')
-
- #update the comment cache
- add_comment(item)
-
- #update the queries
- if g.write_query_queue:
+ if (not commentform.has_errors("comment",
+ errors.BAD_COMMENT,
+ errors.COMMENT_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:
- queries.new_message(item, inbox_rel)
+ to = Account._byID(parent.author_id)
+ subject = parent.subject
+ re = "re: "
+ if not subject.startswith(re):
+ subject = re + subject
+ item, inbox_rel = Message._new(c.user, to, subject,
+ comment, ip, spam)
+ item.parent_id = parent._id
else:
- queries.new_comment(item, inbox_rel)
-
- #set the ratelimiter
- if should_ratelimit:
- VRatelimit.ratelimit(rate_user=True, rate_ip = True, prefix = "rate_comment_")
+ item, inbox_rel = Comment._new(c.user, link, parent_comment,
+ comment, ip, spam)
+ Vote.vote(c.user, item, True, ip)
+ # flag search indexer that something has changed
+ tc.changed(item)
+
+ #update last modified
+ set_last_modified(c.user, 'overview')
+ set_last_modified(c.user, 'commented')
+ set_last_modified(link, 'comments')
+
+ #update the comment cache
+ add_comment(item)
+
+ # clean up the submission form and remove it from the DOM (if reply)
+ t = commentform.find("textarea")
+ t.attr('rows', 3).html("").attr("value", "")
+ if isinstance(parent, (Comment, Message)):
+ commentform.remove()
+ jquery.things(parent._fullname).set_html(".reply-button:first",
+ _("replied"))
+
+ # insert the new comment
+ jquery.insert_things(item)
+ # remove any null listings that may be present
+ jquery("#noresults").hide()
+
+
+ #update the queries
+ if g.write_query_queue:
+ if is_message:
+ queries.new_message(item, inbox_rel)
+ else:
+ queries.new_comment(item, inbox_rel)
+
+ #set the ratelimiter
+ if should_ratelimit:
+ VRatelimit.ratelimit(rate_user=True, rate_ip = True,
+ prefix = "rate_comment_")
+
- @Json
- @validate(VUser(),
- VModhash(),
- VCaptcha(),
- VRatelimit(rate_user = True, rate_ip = True,
- prefix = "rate_share_"),
- share_from = VLength('share_from', length = 100),
- emails = ValidEmails("share_to"),
- reply_to = ValidEmails("replyto", num = 1),
- message = VLength("message", length = 1000),
- thing = VByName('id'))
- def POST_share(self, res, emails, thing, share_from, reply_to,
+ @validatedForm(VUser(),
+ VModhash(),
+ VCaptcha(),
+ VRatelimit(rate_user = True, rate_ip = True,
+ prefix = "rate_share_"),
+ share_from = VLength('share_from', length = 100),
+ emails = ValidEmails("share_to"),
+ reply_to = ValidEmails("replyto", num = 1),
+ message = VLength("message", length = 1000),
+ thing = VByName('parent'))
+ def POST_share(self, shareform, jquery, emails, thing, share_from, reply_to,
message):
# remove the ratelimit error if the user's karma is high
@@ -670,54 +649,51 @@ def POST_share(self, res, emails, thing, share_from, reply_to,
if not should_ratelimit:
c.errors.remove(errors.RATELIMIT)
- res._hide("status_" + thing._fullname)
-
- if res._chk_captcha(errors.BAD_CAPTCHA, thing._fullname):
+ 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.
+ # finding an error on one necessitates hiding the other error
+ if shareform.has_errors("share_from", errors.COMMENT_TOO_LONG):
+ shareform.find(".message-errors").children().hide()
+ elif shareform.has_errors("message", errors.COMMENT_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,
+ errors.NO_EMAILS,
+ 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):
pass
- elif res._chk_error(errors.RATELIMIT, thing._fullname):
- pass
- elif (share_from is None and
- res._chk_error(errors.COMMENT_TOO_LONG,
- 'share_from_' + thing._fullname)):
- res._focus('share_from_' + thing._fullname)
- elif (message is None and
- res._chk_error(errors.COMMENT_TOO_LONG,
- 'message_' + thing._fullname)):
- res._focus('message_' + thing._fullname)
- elif not emails and res._chk_errors((errors.BAD_EMAILS,
- errors.NO_EMAILS,
- errors.TOO_MANY_EMAILS),
- "emails_" + thing._fullname):
- res._focus("emails_" + thing._fullname)
- elif not reply_to and res._chk_error(errors.BAD_EMAILS,
- "replyto_" + thing._fullname):
- res._focus("replyto_" + thing._fullname)
else:
c.user.add_share_emails(emails)
c.user._commit()
-
- res._update("share_li_" + thing._fullname,
- innerHTML=_('shared'))
-
- res._update("sharelink_" + thing._fullname,
- innerHTML=("<div class='clearleft'></div><p class='error'>%s</p>" %
- _("your link has been shared.")))
+ link = jquery.things(thing._fullname)
+ link.set_html(".share", _("shared"))
+ shareform.html("<div class='clearleft'></div>"
+ "<p class='error'>%s</p>" %
+ _("your link has been shared."))
emailer.share(thing, emails, from_name = share_from or "",
body = message or "", reply_to = reply_to or "")
#set the ratelimiter
if should_ratelimit:
- VRatelimit.ratelimit(rate_user=True, rate_ip = True, prefix = "rate_share_")
+ VRatelimit.ratelimit(rate_user=True, rate_ip = True,
+ prefix = "rate_share_")
- @Json
- @validate(VUser(),
- VModhash(),
- vote_type = VVotehash(('vh', 'id')),
- ip = ValidIP(),
- dir = VInt('dir', min=-1, max=1),
- thing = VByName('id'))
- def POST_vote(self, res, dir, thing, ip, vote_type):
+ @noresponse(VUser(),
+ VModhash(),
+ vote_type = VVotehash(('vh', 'id')),
+ ip = ValidIP(),
+ dir = VInt('dir', min=-1, max=1),
+ thing = VByName('id'))
+ def POST_vote(self, dir, thing, ip, vote_type):
ip = request.ip
user = c.user
spam = (c.user._spam or
@@ -746,42 +722,40 @@ def POST_vote(self, res, dir, thing, ip, vote_type):
# flag search indexer that something has changed
tc.changed(thing)
- @Json
- @validate(VUser(),
- VModhash(),
- stylesheet_contents = nop('stylesheet_contents'),
- op = VOneOf('op',['save','preview']))
- def POST_subreddit_stylesheet(self, res, stylesheet_contents = '', op='save'):
+ @validatedForm(VUser(),
+ VModhash(),
+ # nop is safe: handled after auth checks below
+ stylesheet_contents = nop('stylesheet_contents'),
+ op = VOneOf('op',['save','preview']))
+ def POST_subreddit_stylesheet(self, form, jquery,
+ stylesheet_contents = '', op='save'):
if not c.site.can_change_stylesheet(c.user):
return self.abort(403,'forbidden')
if g.css_killswitch:
return self.abort(403,'forbidden')
+ # validation is expensive. Validate after we've confirmed
+ # that the changes will be allowed
parsed, report = cssfilter.validate_css(stylesheet_contents)
if report.errors:
error_items = [ CssError(x).render(style='html')
for x in sorted(report.errors) ]
-
- res._update('status', innerHTML = _('validation errors'))
- res._update('validation-errors', innerHTML = ''.join(error_items))
- res._show('error-header')
+ form.set_html(".status", _('validation errors'))
+ form.set_html(".errors ul", ''.join(error_items))
+ form.find('.errors').show()
else:
- res._hide('error-header')
- res._update('status', innerHTML = '')
- res._update('validation-errors', innerHTML = '')
+ form.find('.errors').hide()
+ form.set_html(".errors ul", '')
stylesheet_contents_parsed = parsed.cssText if parsed else ''
# if the css parsed, we're going to apply it (both preview & save)
if not report.errors:
- res._call('applyStylesheet("%s"); ' %
- stylesheet_contents_parsed.replace('"', r"\"").replace("\n", r"\n").replace("\r", r"\r"))
+ jquery.apply_stylesheet(stylesheet_contents_parsed)
if not report.errors and op == 'save':
- stylesheet_contents_user = stylesheet_contents
-
c.site.stylesheet_contents = stylesheet_contents_parsed
- c.site.stylesheet_contents_user = stylesheet_contents_user
+ c.site.stylesheet_contents_user = stylesheet_contents
c.site.stylesheet_hash = md5(stylesheet_contents_parsed).hexdigest()
@@ -789,43 +763,41 @@ def POST_subreddit_stylesheet(self, res, stylesheet_contents = '', op='save'):
tc.changed(c.site)
c.site._commit()
- res._update('status', innerHTML = 'saved')
- res._update('validation-errors', innerHTML = '')
+ form.set_html(".status", _('saved'))
+ form.set_html(".errors ul", "")
elif op == 'preview':
# try to find a link to use, otherwise give up and
# return
links = cssfilter.find_preview_links(c.site)
- if not links:
- # we're probably not going to be able to find any
- # comments, either; screw it
- return
-
- res._show('preview-table')
-
- # do a regular link
- cssfilter.rendered_link('preview_link_normal',
- res, links,
- media = 'off', compress=False)
- # now do one with media
- cssfilter.rendered_link('preview_link_media',
- res, links,
- media = 'on', compress=False)
- # do a compressed link
- cssfilter.rendered_link('preview_link_compressed',
- res, links,
- media = 'off', compress=True)
+ if links:
+
+ jquery('#preview-table').show()
+
+ # do a regular link
+ jquery('#preview_link_normal').html(
+ cssfilter.rendered_link(links, media = 'off',
+ compress=False))
+ # now do one with media
+ jquery('#preview_link_media').html(
+ cssfilter.rendered_link(links, media = 'on',
+ compress=False))
+ # do a compressed link
+ jquery('#preview_link_compressed').html(
+ cssfilter.rendered_link(links, media = 'off',
+ compress=True))
+
# and do a comment
comments = cssfilter.find_preview_comments(c.site)
- if not comments:
- return
- cssfilter.rendered_comment('preview_comment',res,comments)
+ if comments:
+ jquery('#preview_comment').html(
+ cssfilter.rendered_comment(comments))
- @Json
- @validate(VSrModerator(),
- VModhash(),
- name = VCssName('img_name'))
- def POST_delete_sr_img(self, res, name):
+
+ @validatedForm(VSrModerator(),
+ VModhash(),
+ name = VCssName('img_name'))
+ def POST_delete_sr_img(self, form, jquery, name):
"""
Called called upon requested delete on /about/stylesheet.
Updates the site's image list, and causes the <li> which wraps
@@ -836,16 +808,11 @@ def POST_delete_sr_img(self, res, name):
return self.abort(403,'forbidden')
c.site.del_image(name)
c.site._commit()
- # hide the image and it's container
- res._hide("img-li_%s" % name)
- # reset the status
- res._update('img-status', innerHTML = _("deleted"))
- @Json
- @validate(VSrModerator(),
- VModhash())
- def POST_delete_sr_header(self, res):
+ @validatedForm(VSrModerator(),
+ VModhash())
+ def POST_delete_sr_header(self, form, jquery):
"""
Called when the user request that the header on a sr be reset.
"""
@@ -856,14 +823,13 @@ def POST_delete_sr_header(self, res):
c.site.header = None
c.site._commit()
# reset the header image on the page
- res._update('header-img', src = DefaultSR.header)
+ form.find('#header-img').attr("src", DefaultSR.header)
# hide the button which started this
- res._hide ('delete-img')
+ form.find('#delete-img').hide()
# hide the preview box
- res._hide ('img-preview-container')
+ form.find('#img-preview-container').hide()
# reset the status boxes
- res._update('img-status', innerHTML = _("deleted"))
- res._update('status', innerHTML = "")
+ form.set_html('.img-status', _("deleted"))
def GET_upload_sr_img(self, *a, **kw):
@@ -931,29 +897,31 @@ def POST_upload_sr_img(self, file, header, name):
new_url = cssfilter.save_sr_image(c.site, cleaned, num = num)
if header:
c.site.header = new_url
- c.site._commit()
+ c.site._commit()
return UploadedImage(_('saved'), new_url, name,
errors = errors).render()
- @Json
- @validate(VUser(),
- VModhash(),
- VRatelimit(rate_user = True,
- rate_ip = True,
- prefix = 'create_reddit_'),
- sr = VByName('sr'),
- name = VSubredditName("name"),
- title = VSubredditTitle("title"),
- domain = VCnameDomain("domain"),
- description = VSubredditDesc("description"),
- lang = VLang("lang"),
- over_18 = VBoolean('over_18'),
- show_media = VBoolean('show_media'),
- type = VOneOf('type', ('public', 'private', 'restricted'))
- )
- def POST_site_admin(self, res, name ='', sr = None, **kw):
+ @validatedForm(VUser(),
+ VModhash(),
+ VRatelimit(rate_user = True,
+ rate_ip = True,
+ prefix = 'create_reddit_'),
+ sr = VByName('sr'),
+ name = VSubredditName("name"),
+ title = VSubredditTitle("title"),
+ domain = VCnameDomain("domain"),
+ description = VSubredditDesc("description"),
+ lang = VLang("lang"),
+ over_18 = VBoolean('over_18'),
+ show_media = VBoolean('show_media'),
+ type = VOneOf('type', ('public', 'private', 'restricted'))
+ )
+ def POST_site_admin(self, form, jquery, name ='', sr = None, **kw):
+ # the status button is outside the form -- have to reset by hand
+ form.parent().set_html('.status', "")
+
redir = False
kw = dict((k, v) for k, v in kw.iteritems()
if k in ('name', 'title', 'domain', 'description', 'over_18',
@@ -967,30 +935,20 @@ def POST_site_admin(self, res, name ='', sr = None, **kw):
domain = kw['domain']
cname_sr = domain and Subreddit._by_domain(domain)
if cname_sr and (not sr or sr != cname_sr):
- c.errors.add(errors.USED_CNAME)
+ c.errors.add(errors.USED_CNAME)
- if not sr and res._chk_error(errors.RATELIMIT):
- pass
- elif not sr and res._chk_errors((errors.SUBREDDIT_EXISTS,
- errors.BAD_SR_NAME)):
- res._hide('example_name')
- res._focus('name')
- elif res._chk_errors((errors.NO_TITLE, errors.TITLE_TOO_LONG)):
- res._hide('example_title')
- res._focus('title')
- elif res._chk_error(errors.INVALID_OPTION):
+ if not sr and form.has_errors(None, errors.RATELIMIT):
pass
- elif res._chk_errors((errors.BAD_CNAME, errors.USED_CNAME)):
- res._hide('example_domain')
- res._focus('domain')
- elif res._chk_error(errors.DESC_TOO_LONG):
- res._focus('description')
-
- res._update('status', innerHTML = '')
-
- if res.error:
+ 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):
+ 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)):
pass
-
#creating a new reddit
elif not sr:
#sending kw is ok because it was sanitized above
@@ -1020,86 +978,82 @@ def POST_site_admin(self, res, name ='', sr = None, **kw):
# flag search indexer that something has changed
tc.changed(sr)
-
- res._update('status', innerHTML = _('saved'))
-
+ form.parent().set_html('.status', _("saved"))
if redir:
- res._redirect(redir)
+ jquery.redirect(redir)
- @Json
- @validate(VModhash(),
- VSrCanBan('id'),
- thing = VByName('id'))
- def POST_ban(self, res, thing):
+ @noresponse(VModhash(),
+ VSrCanBan('id'),
+ thing = VByName('id'))
+ def POST_ban(self, thing):
thing.moderator_banned = not c.user_is_admin
thing.banner = c.user.name
thing._commit()
# NB: change table updated by reporting
unreport(thing, correct=True, auto=False)
- @Json
- @validate(VModhash(),
- VSrCanBan('id'),
- thing = VByName('id'))
- def POST_unban(self, res, thing):
+ @noresponse(VModhash(),
+ VSrCanBan('id'),
+ thing = VByName('id'))
+ def POST_unban(self, thing):
# NB: change table updated by reporting
unreport(thing, correct=False)
- @Json
- @validate(VModhash(),
- VSrCanBan('id'),
- thing = VByName('id'))
- def POST_ignore(self, res, thing):
+ @noresponse(VModhash(),
+ VSrCanBan('id'),
+ thing = VByName('id'))
+ def POST_ignore(self, thing):
# NB: change table updated by reporting
unreport(thing, correct=False)
- @Json
- @validate(VUser(),
- VModhash(),
- thing = VByName('id'))
- def POST_save(self, res, thing):
+ @noresponse(VUser(),
+ VModhash(),
+ thing = VByName('id'))
+ def POST_save(self, thing):
r = thing._save(c.user)
if g.write_query_queue:
queries.new_savehide(r)
- @Json
- @validate(VUser(),
- VModhash(),
- thing = VByName('id'))
- def POST_unsave(self, res, thing):
+ @noresponse(VUser(),
+ VModhash(),
+ thing = VByName('id'))
+ def POST_unsave(self, thing):
r = thing._unsave(c.user)
if g.write_query_queue and r:
queries.new_savehide(r)
- @Json
- @validate(VUser(),
- VModhash(),
- thing = VByName('id'))
- def POST_hide(self, res, thing):
+ @noresponse(VUser(),
+ VModhash(),
+ thing = VByName('id'))
+ def POST_hide(self, thing):
r = thing._hide(c.user)
if g.write_query_queue:
queries.new_savehide(r)
- @Json
- @validate(VUser(),
- VModhash(),
- thing = VByName('id'))
- def POST_unhide(self, res, thing):
+ @noresponse(VUser(),
+ VModhash(),
+ thing = VByName('id'))
+ def POST_unhide(self, thing):
r = thing._unhide(c.user)
if g.write_query_queue and r:
queries.new_savehide(r)
- @Json
- @validate(link = VByName('link_id'),
- sort = VMenu('where', CommentSortMenu),
- children = VCommentIDs('children'),
- depth = VInt('depth', min = 0, max = 8),
- mc_id = nop('id'))
- def POST_morechildren(self, res, link, sort, children, depth, mc_id):
+ @validatedForm(link = VByName('link_id'),
+ sort = VMenu('where', CommentSortMenu),
+ children = VCommentIDs('children'),
+ depth = VInt('depth', min = 0, max = 8),
+ mc_id = nop('id'))
+ def POST_morechildren(self, form, jquery,
+ link, sort, children, depth, mc_id):
+ user = c.user if c.user_is_loggedin else None
+ if not link or not link.subreddit_slow.can_view(user):
+ return self.abort(403,'forbidden')
+
if children:
- builder = CommentBuilder(link, CommentSortMenu.operator(sort), children)
+ builder = CommentBuilder(link, CommentSortMenu.operator(sort),
+ children)
items = builder.get_items(starting_depth = depth, num = 20)
def _children(cur_items):
items = []
@@ -1114,7 +1068,7 @@ def _children(cur_items):
return items
# assumes there is at least one child
-# a = _children(items[0].child.things)
+ # a = _children(items[0].child.things)
a = []
for item in items:
a.append(item)
@@ -1124,16 +1078,16 @@ def _children(cur_items):
# the result is not always sufficient to replace the
# morechildren link
- if mc_id not in [x._fullname for x in a]:
- res._hide('thingrow_' + str(mc_id))
- res._send_things(a)
+ jquery.things(str(mc_id)).remove()
+ jquery.insert_things(a, append = True)
- @validate(uh = nop('uh'),
+ @validate(uh = nop('uh'), # VModHash() will raise, check manually
action = VOneOf('what', ('like', 'dislike', 'save')),
links = VUrl(['u']))
def GET_bookmarklet(self, action, uh, links):
- '''Controller for the functionality of the bookmarklets (not the distribution page)'''
+ '''Controller for the functionality of the bookmarklets (not
+ the distribution page)'''
# the redirect handler will clobber the extension if not told otherwise
c.extension = "png"
@@ -1157,7 +1111,8 @@ def GET_bookmarklet(self, action, uh, links):
if action in ['like', 'dislike']:
#vote up all of the links
for link in links:
- v = Vote.vote(c.user, link, action == 'like', request.ip)
+ v = Vote.vote(c.user, link, action == 'like',
+ request.ip)
if g.write_query_queue:
queries.new_vote(v)
elif action == 'save':
@@ -1169,81 +1124,60 @@ def GET_bookmarklet(self, action, uh, links):
return self.redirect("/static/css_submit.png")
- @Json
- @validate(user = VUserWithEmail('name'))
- def POST_password(self, res, user):
- res._update('status', innerHTML = '')
- if res._chk_error(errors.USER_DOESNT_EXIST):
- res._focus('name')
- elif res._chk_error(errors.NO_EMAIL_FOR_USER):
- res._focus('name')
- else:
+ @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):
emailer.password_email(user)
- res._success()
- @Json
- @validate(user = VCacheKey('reset', ('key', 'name')),
- key= nop('key'),
- password = VPassword(['passwd', 'passwd2']))
- def POST_resetpassword(self, res, user, key, password):
- res._update('status', innerHTML = '')
- if res._chk_error(errors.BAD_PASSWORD):
- res._focus('passwd')
- elif res._chk_error(errors.BAD_PASSWORD_MATCH):
- res._focus('passwd2')
- elif errors.BAD_USERNAME in c.errors:
- cache.delete(str('reset_%s' % key))
- return res._redirect('/password')
- elif user:
- cache.delete(str('reset_%s' % key))
+ @validatedForm(user = VCacheKey('reset', ('key', 'name')),
+ password = VPassword(['passwd', 'passwd2']))
+ def POST_resetpassword(self, form, jquery, user, key, password):
+ if errors.BAD_USERNAME in c.errors:
+ return jquery.redirect('/password')
+ elif (not form.has_errors('passwd', errors.BAD_PASSWORD) and
+ not form.has_errors('passwd2', errors.BAD_PASSWORD_MATCH) and
+ user):
change_password(user, password)
- self._login(res, user, '/resetpassword')
+ self._login(jquery, user, '/resetpassword')
- @Json
- @validate(VUser())
- def POST_frame(self, res):
- c.user.pref_frame = True
+ @noresponse(VUser())
+ def POST_noframe(self):
+ """
+ removes the reddit toolbar if that currently the user's preference
+ """
+ c.user.pref_frame = False
c.user._commit()
- @Json
- @validate(VUser())
- def POST_noframe(self, res):
- c.user.pref_frame = False
+ @noresponse(VUser())
+ def POST_frame(self):
+ """
+ undoes POST_noframe
+ """
+ c.user.pref_frame = True
c.user._commit()
- @Json
- @validate(VUser(),
- where=nop('where'),
- sort = nop('sort'))
- def POST_sort(self, res, where, sort):
- if where.startswith('sort_'):
- setattr(c.user, where, sort)
- c.user._commit()
- @Json
- def POST_new_captcha(self, res, *a, **kw):
- res.captcha = dict(iden = get_iden(), refresh = True)
-
- @Json
- @validate(VAdmin(),
- l = nop('id'))
- def POST_deltranslator(self, res, l):
- lang, a = l.split('_')
- if a and Translator.exists(lang):
- tr = Translator(locale = lang)
- tr.author.remove(a)
- tr.save()
+ @validatedForm()
+ def POST_new_captcha(self, form, jquery, *a, **kw):
+ jquery("body").captcha(get_iden())
+ @noresponse(VAdmin(),
+ tr = VTranslation("id"),
+ user = nop('user'))
+ def POST_deltranslator(self, tr, user):
+ if tr:
+ tr.author.remove(user)
+ tr.save()
- @Json
- @validate(VUser(),
- VModhash(),
- action = VOneOf('action', ('sub', 'unsub')),
- sr = VByName('sr'))
- def POST_subscribe(self, res, action, sr):
+ @noresponse(VUser(),
+ VModhash(),
+ action = VOneOf('action', ('sub', 'unsub')),
+ sr = VByName('sr'))
+ def POST_subscribe(self, action, sr):
self._subscribe(sr, action == 'sub')
def _subscribe(self, sr, sub):
@@ -1258,106 +1192,90 @@ def _subscribe(self, sr, sub):
tc.changed(sr)
- @Json
- @validate(VAdmin(),
- lang = nop("id"))
- def POST_disable_lang(self, res, lang):
- if lang and Translator.exists(lang):
- tr = Translator(locale = lang)
+ @noresponse(VAdmin(),
+ tr = VTranslation("id"))
+ def POST_disable_lang(self, tr):
+ if tr:
tr._is_enabled = False
- @Json
- @validate(VAdmin(),
- lang = nop("id"))
- def POST_enable_lang(self, res, lang):
- if lang and Translator.exists(lang):
- tr = Translator(locale = lang)
+ @noresponse(VAdmin(),
+ tr = VTranslation("id"))
+ def POST_enable_lang(self, lang):
+ if tr:
tr._is_enabled = True
- def action_cookie(action):
- s = action + request.ip + request.user_agent
- return sha.new(s).hexdigest()
-
- @Json
- @validate(num_margin = VCssMeasure('num_margin'),
- mid_margin = VCssMeasure('mid_margin'),
- links = VFullNames('links'))
- def POST_fetch_links(self, res, num_margin, mid_margin, links):
- b = IDBuilder([l._fullname for l in links],
+ @validatedForm(links = VFullNames('links'))
+ def POST_fetch_links(self, form, jquery, links):
+ b = IDBuilder([l._fullname for l in links],
wrap = ListingController.builder_wrapper)
l = OrganicListing(b)
- l.num_margin = num_margin
- l.mid_margin = mid_margin
- res.object = res._thing(l.listing(), action = 'populate')
+ l.num_margin = 0
+ l.mid_margin = 0
+ jquery.replace_things(l, stubs = True)
- @Json
- @validate(VUser(),
+ @noresponse(VUser(),
ui_elem = VOneOf('id', ('organic',)))
- def POST_disable_ui(self, res, ui_elem):
+ def POST_disable_ui(self, ui_elem):
if ui_elem:
pref = "pref_%s" % ui_elem
if getattr(c.user, pref):
setattr(c.user, "pref_" + ui_elem, False)
c.user._commit()
- @Json