This repository has been archived by the owner. It is now read-only.
Permalink
Browse files

Upgrade Instructions

====
   * Uninstall the python Cassandra package that we previously depended on (it has a namespace conflict with the new package we depend on).  To find it:

     $ python -c "import cassandra; print cassandra.__file__"

    and rm -r the .egg directory after "site-packages/"

   * This version relies on cython, so if "make" fails, you may have to install cython via your distro's package manager.

     $ cd reddit/r2
     $ python setup.py develop # possibly with "sudo" depending on your install
     $ make

   * Cassandra is now required for the caching layer.  An example storage-conf.xml can be found in reddit/srv/cassandra.  Make sure that the additional <Keyspace> items are included in your conf file.

   * remove the query_queue_reader services if they are running.  add new gen_time_listings.sh instead.  Suggested cron:

    0    */2 *   *   *   $SCRIPTS/gen_time_listings.sh year '("month","year")'
    */5  *   *   *   *   $SCRIPTS/gen_time_listings.sh week '("day","week")'
    *    *   *   *   *   $SCRIPTS/gen_time_listings.sh hour '("hour",)'

   where $SCRIPTS is the location of this script directory

Features and Bugfixes
====
 * Mobile reddit:
   * templates are in r2/templates of the form *.compact
   * css is r2/public/static/css/compact.css
     * beginning of a sass-based (http://sass-lang.com/) compact.scss
   * reachable via .compact extension or from the "i" subdomain.
 * Cassandra is now *required*, and votes are currently written out to both cassandra and postgres (part of an eventual migration).
 * attempt to make the db connection code a little smarter.
   * A dropped DB connection will mark the connection dead and randomly attempt to reconnect.
   * A dropped db connection on start will permanently mark the connection as dead.
 * Calculate the time-filtered top/controversy listings using mapreduce instead of prec_links (new cron job in reddit/scripts)
 * allow default user/pass for database to be specified with '*' to fallback on db_user and db_pass in the INI file
 * Search feedback buttons
 * make deleted comments not show up in your inbox.
 * move last_visited into cassandra
 * Swallow rare, race-conditiony POST_save/hide/subscribe problems
 * Apparently we haven't been breaking properly for the past few weeks.
  • Loading branch information...
KeyserSosa committed Jun 16, 2010
1 parent 08c431b commit 9a4271f641039f03c62a24c267e886fd0d86b350
Showing with 9,256 additions and 1,434 deletions.
  1. +10 −0 .gitignore
  2. +12 −4 r2/Makefile
  3. +2 −2 r2/example.ini
  4. +54 −0 r2/pyx_setup.py
  5. +23 −15 r2/r2/config/middleware.py
  6. +5 −1 r2/r2/config/routing.py
  7. +1 −0 r2/r2/config/templates.py
  8. +43 −25 r2/r2/controllers/api.py
  9. +6 −0 r2/r2/controllers/buttons.py
  10. +1 −1 r2/r2/controllers/error.py
  11. +36 −0 r2/r2/controllers/front.py
  12. +34 −0 r2/r2/controllers/health.py
  13. +8 −2 r2/r2/controllers/listingcontroller.py
  14. +67 −50 r2/r2/controllers/reddit_base.py
  15. +12 −4 r2/r2/controllers/validator/validator.py
  16. +113 −0 r2/r2/lib/_normalized_hot.pyx
  17. +6 −2 r2/r2/lib/amqp.py
  18. +68 −61 r2/r2/lib/app_globals.py
  19. +0 −4 r2/r2/lib/authorize/api.py
  20. +92 −169 r2/r2/lib/cache.py
  21. +2 −2 r2/r2/lib/cssfilter.py
  22. +66 −0 r2/r2/lib/db/_sorts.pyx
  23. +112 −199 r2/r2/lib/db/queries.py
  24. +0 −60 r2/r2/lib/db/query_queue.py
  25. +2 −55 r2/r2/lib/db/sorts.py
  26. +579 −0 r2/r2/lib/db/tdb_cassandra.py
  27. +29 −70 r2/r2/lib/db/tdb_sql.py
  28. +19 −42 r2/r2/lib/db/thing.py
  29. +108 −0 r2/r2/lib/indextank.py
  30. +135 −0 r2/r2/lib/indextankupdate.py
  31. +1 −1 r2/r2/lib/jsontemplates.py
  32. +18 −2 r2/r2/lib/lock.py
  33. +1 −1 r2/r2/lib/log.py
  34. +121 −5 r2/r2/lib/manager/db_manager.py
  35. +7 −9 r2/r2/lib/memoize.py
  36. 0 r2/r2/lib/migrate/__init__.py
  37. 0 r2/r2/lib/{ → migrate}/migrate.py
  38. +223 −0 r2/r2/lib/migrate/mr_permacache.py
  39. +62 −0 r2/r2/lib/mr_tools.py
  40. +127 −0 r2/r2/lib/mr_top.py
  41. +10 −133 r2/r2/lib/normalized_hot.py
  42. +1 −1 r2/r2/lib/organic.py
  43. +4 −3 r2/r2/lib/pages/graph.py
  44. +90 −30 r2/r2/lib/pages/pages.py
  45. +8 −4 r2/r2/lib/queues.py
  46. +40 −0 r2/r2/lib/sgm.pyx
  47. +1 −1 r2/r2/lib/solrsearch.py
  48. +1 −1 r2/r2/lib/strings.py
  49. +9 −4 r2/r2/lib/template_helpers.py
  50. +10 −4 r2/r2/lib/tracking.py
  51. +1 −1 r2/r2/lib/translation.py
  52. +33 −3 r2/r2/lib/utils/thing_utils.py
  53. +59 −24 r2/r2/lib/{wrapped.py → wrapped.pyx}
  54. +330 −0 r2/r2/models/_builder.pyx
  55. +10 −7 r2/r2/models/account.py
  56. +1 −1 r2/r2/models/bidding.py
  57. +32 −330 r2/r2/models/builder.py
  58. +7 −2 r2/r2/models/link.py
  59. +1 −1 r2/r2/models/mail_queue.py
  60. +30 −10 r2/r2/models/subreddit.py
  61. +77 −8 r2/r2/models/vote.py
  62. BIN r2/r2/public/static/bookmarklet/iphone_edit_the_button.png
  63. BIN r2/r2/public/static/bookmarklet/iphone_find_the_underscore.png
  64. BIN r2/r2/public/static/bookmarklet/iphone_it_should_look_like_this.png
  65. BIN r2/r2/public/static/bookmarklet/iphone_push_the_button.png
  66. BIN r2/r2/public/static/compact/arrows.png
  67. BIN r2/r2/public/static/compact/mobilesprite.png
  68. BIN r2/r2/public/static/compact/options_icons.png
  69. BIN r2/r2/public/static/compact/reddit-apple-mobile-device.png
  70. BIN r2/r2/public/static/compact/reddit_startimg.png
  71. BIN r2/r2/public/static/compact/youbrokereddit.png
  72. +1,624 −0 r2/r2/public/static/css/compact.css
  73. +1,468 −0 r2/r2/public/static/css/compact.scss
  74. +266 −5 r2/r2/public/static/css/reddit.css
  75. +7 −0 r2/r2/public/static/js/autoresize.jquery.min.js
  76. +204 −0 r2/r2/public/static/js/compact.js
  77. +6 −1 r2/r2/public/static/js/jquery.reddit.js
  78. +30 −14 r2/r2/public/static/js/reddit.js
  79. BIN r2/r2/public/static/reddit_loading.png
  80. BIN r2/r2/public/static/reddit_mobile/img/screen1.png
  81. BIN r2/r2/public/static/reddit_mobile/img/screen1_small.png
  82. BIN r2/r2/public/static/reddit_mobile/img/screen2.png
  83. BIN r2/r2/public/static/reddit_mobile/img/screen2_small.png
  84. BIN r2/r2/public/static/reddit_mobile/img/screen3.png
  85. BIN r2/r2/public/static/reddit_mobile/img/screen3_small.png
  86. BIN r2/r2/public/static/reddit_mobile/img/screen4.png
  87. BIN r2/r2/public/static/reddit_mobile/img/screen4_small.png
  88. +279 −0 r2/r2/public/static/reddit_mobile/index.htm
  89. +66 −0 r2/r2/templates/base.compact
  90. +1 −0 r2/r2/templates/base.mobile
  91. +42 −0 r2/r2/templates/bookmarklets.compact
  92. +99 −0 r2/r2/templates/comment.compact
  93. +23 −0 r2/r2/templates/errorpage.compact
  94. +60 −0 r2/r2/templates/frame.compact
  95. +56 −0 r2/r2/templates/frametoolbar.compact
  96. 0 r2/r2/templates/infobar.compact
  97. +117 −0 r2/r2/templates/link.compact
  98. +9 −6 r2/r2/templates/link.html
  99. +13 −5 r2/r2/templates/link.mobile
  100. +51 −0 r2/r2/templates/listing.compact
  101. +29 −0 r2/r2/templates/login.compact
  102. +1 −4 r2/r2/templates/login.html
  103. 0 r2/r2/templates/mediaembed.compact
  104. +26 −0 r2/r2/templates/menuarea.compact
  105. +110 −0 r2/r2/templates/message.compact
  106. +58 −0 r2/r2/templates/messagecompose.compact
  107. +33 −0 r2/r2/templates/morechildren.compact
  108. +1 −1 r2/r2/templates/morechildren.html
  109. +25 −0 r2/r2/templates/morerecursion.compact
  110. +39 −0 r2/r2/templates/navbutton.compact
  111. +126 −0 r2/r2/templates/navmenu.compact
  112. +104 −0 r2/r2/templates/newlink.compact
  113. +3 −1 r2/r2/templates/newlink.html
  114. +49 −0 r2/r2/templates/pagenamenav.compact
  115. +23 −0 r2/r2/templates/panestack.compact
  116. 0 r2/r2/templates/permalinkmessage.compact
  117. +2 −2 r2/r2/templates/prefoptions.html
  118. +51 −0 r2/r2/templates/printable.compact
  119. +2 −2 r2/r2/templates/printable.html
  120. +55 −0 r2/r2/templates/reddit.compact
  121. +13 −1 r2/r2/templates/reddit.mobile
  122. +97 −0 r2/r2/templates/redditheader.compact
  123. +1 −1 r2/r2/templates/redditheader.mobile
  124. +32 −0 r2/r2/templates/register.compact
  125. +1 −0 r2/r2/templates/register.html
  126. +49 −0 r2/r2/templates/searchbar.compact
  127. +28 −14 r2/r2/templates/searchbar.html
  128. +23 −0 r2/r2/templates/searchfail.compact
  129. +23 −0 r2/r2/templates/searchform.compact
  130. +22 −0 r2/r2/templates/sharelink.compact
  131. +67 −0 r2/r2/templates/subreddit.compact
  132. +23 −0 r2/r2/templates/takedownpane.compact
  133. +54 −0 r2/r2/templates/trycompact.compact
  134. +25 −0 r2/r2/templates/unfoundpage.compact
  135. +148 −0 r2/r2/templates/usertext.compact
  136. +31 −0 r2/r2/templates/usertext.mobile
  137. +2 −0 r2/r2/templates/utils.html
  138. +23 −0 r2/r2/templates/wrappeduser.compact
  139. +23 −13 r2/setup.py
  140. +1 −1 r2/supervise_watcher.py
  141. +57 −0 scripts/indextank_backfill.py
  142. +395 −0 srv/cassandra/storage-conf.xml
  143. +0 −3 srv/query_queue_worker1/log/run
  144. +0 −6 srv/query_queue_worker1/run
View
@@ -31,3 +31,13 @@ r2/myproduction.ini
.DS_Store
r2/r2.egg-info/**
r2/r2/public/static/sprite.png
+r2/_builder.egg-info/
+r2/_normalized_hot.egg-info/
+r2/_sorts.egg-info/
+r2/r2/lib/_normalized_hot.c
+r2/r2/lib/db/_sorts.c
+r2/r2/lib/sgm.c
+r2/r2/lib/wrapped.c
+r2/r2/models/_builder.c
+r2/sgm.egg-info/
+r2/wrapped.egg-info/
View
@@ -21,10 +21,10 @@
################################################################################
# Jacascript files to be compressified
-js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js ui.core.js ui.datepicker.js sponsored.js jquery.flot.js jquery.lazyload.js
+js_targets = jquery.js jquery.json.js jquery.reddit.js reddit.js ui.core.js ui.datepicker.js sponsored.js jquery.flot.js compact.js
# CSS targets
main_css = reddit.css
-css_targets = reddit-ie6-hax.css reddit-ie7-hax.css mobile.css spreadshirt.css
+css_targets = reddit-ie6-hax.css reddit-ie7-hax.css mobile.css spreadshirt.css compact.css
SED=sed
CAT=cat
@@ -47,6 +47,9 @@ CSSTARGETS := $(foreach css, $(css_targets), $(static_dir)/$(css))
MAINCSS := $(foreach css, $(main_css), $(static_dir)/$(css))
RTLCSS = $(CSSTARGETS:.css=-rtl.css) $(MAINCSS:.css=-rtl.css)
+PYX_FILES := $(shell find . -name \*.pyx)
+PYXSO_FILES := $(PYX_FILES:.pyx=.so)
+
MD5S = $(JSTARGETS:=.md5) $(CSSTARGETS:=.md5) $(MAINCSS:=.md5) $(RTLCSS:=.md5)
@@ -63,7 +66,7 @@ else
endif
-all: $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(RTLCSS) $(INIS)
+all: $(JSTARGETS) $(CSSTARGETS) $(MD5S) $(RTLCSS) $(INIS) $(PYXSO_FILES)
.PHONY: js css md5 rtl clean all
@@ -85,7 +88,12 @@ $(RTLCSS): %-rtl.css : %.css
-e "s/>####</right/g" \
-e "s/\(margin\|padding\):\s*\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)\s\+\([^; ]\+\)/\1:\2 \5 \4 \3/g" $< > $@
-
+$(PYXSO_FILES): %.so : %.pyx
+ rm -f $(<:.pyx=.pyc)
+ cython $<
+ mkdir -p tmp
+ PYTHONPATH=tmp python pyx_setup.py develop -d tmp -b tmp $<
+ rm -r tmp
js: $(JSTARGETS)
View
@@ -91,7 +91,7 @@ servicecaches = 127.0.0.1:11211
# memcaches that sit in front of cassandra
permacache_memcaches = 127.0.0.1:11211
# cassandra hosts. one of these will be chosen at random by pycassa
-cassandra_seeds =
+cassandra_seeds = 127.0.0.1:9160
# -- url cache options --
url_caches = 127.0.0.1:11211
@@ -338,5 +338,5 @@ beaker.session_secret = somesecret
# WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT*
# Debug mode will enable the interactive debugging tool, allowing ANYONE to
# execute malicious code after an exception is raised.
-#set debug = false
+set debug = true
View
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+# 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-2010
+# CondeNet, Inc. All Rights Reserved.
+################################################################################
+
+from ez_setup import use_setuptools
+use_setuptools()
+from setuptools import find_packages#, setup
+from distutils.core import setup, Extension
+from Cython.Distutils import build_ext
+import os, sys
+import shutil
+
+def build_so(pyx_file):
+ if not pyx_file.endswith(".pyx"):
+ raise ValueError, "expected a pyx file, got %s" % pyx_file
+ if not os.path.exists(pyx_file):
+ raise ValueError, "pyx file does not exist: %s" % pyx_file
+ lib = pyx_file[:-4].replace('/', '.').lstrip(".")
+ name = lib.split('.')[-1]
+ ext_modules = [ Extension(lib, [pyx_file]) ]
+ setup(
+ name=name,
+ version="",
+ packages=find_packages(),
+ include_package_data=True,
+ test_suite = 'nose.collector',
+ cmdclass = {'build_ext': build_ext},
+ ext_modules = ext_modules,
+ )
+ shutil.rmtree(name + '.egg-info')
+
+
+if __name__ == "__main__":
+ build_so(sys.argv.pop())
View
@@ -9,7 +9,7 @@
#
# 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 specific language governing rig and limitations under the License.
#
# The Original Code is Reddit.
#
@@ -43,6 +43,7 @@
#from pylons.middleware import error_mapper
def error_mapper(code, message, environ, global_conf=None, **kw):
+ from pylons import c
if environ.get('pylons.error_call'):
return None
else:
@@ -65,7 +66,6 @@ def error_mapper(code, message, environ, global_conf=None, **kw):
#preserve x-sup-id when 304ing
if code == 304:
- from pylons import c
#check to see if c is useable
try:
c.test
@@ -311,6 +311,10 @@ def __call__(self, environ, start_response):
# subdomains which change the extension
elif sd == 'm':
environ['reddit-domain-extension'] = 'mobile'
+ elif sd == 'I':
+ environ['reddit-domain-extension'] = 'compact'
+ elif sd == 'i':
+ environ['reddit-domain-extension'] = 'compact'
elif sd in ('api', 'rss', 'xml', 'json'):
environ['reddit-domain-extension'] = sd
elif (len(sd) == 2 or (len(sd) == 5 and sd[2] == '-')) and self.lang_re.match(sd):
@@ -368,26 +372,30 @@ def __call__(self, environ, start_response):
class ExtensionMiddleware(object):
ext_pattern = re.compile(r'\.([^/]+)$')
- extensions = {'rss' : ('xml', 'text/xml; charset=UTF-8'),
- 'xml' : ('xml', 'text/xml; charset=UTF-8'),
- 'js' : ('js', 'text/javascript; charset=UTF-8'),
- 'wired' : ('wired', 'text/javascript; charset=UTF-8'),
- 'embed' : ('htmllite', 'text/javascript; charset=UTF-8'),
- 'mobile' : ('mobile', 'text/html; charset=UTF-8'),
- 'png' : ('png', 'image/png'),
- 'css' : ('css', 'text/css'),
- 'csv' : ('csv', 'text/csv; charset=UTF-8'),
- 'api' : (api_type(), 'application/json; charset=UTF-8'),
- 'json' : (api_type(), 'application/json; charset=UTF-8'),
- 'json-html' : (api_type('html'), 'application/json; charset=UTF-8')}
+ extensions = (('rss' , ('xml', 'text/xml; charset=UTF-8')),
+ ('xml' , ('xml', 'text/xml; charset=UTF-8')),
+ ('js' , ('js', 'text/javascript; charset=UTF-8')),
+ ('wired' , ('wired', 'text/javascript; charset=UTF-8')),
+ ('embed' , ('htmllite', 'text/javascript; charset=UTF-8')),
+ ('mobile' , ('mobile', 'text/html; charset=UTF-8')),
+ ('png' , ('png', 'image/png')),
+ ('css' , ('css', 'text/css')),
+ ('csv' , ('csv', 'text/csv; charset=UTF-8')),
+ ('api' , (api_type(), 'application/json; charset=UTF-8')),
+ ('json-html' , (api_type('html'), 'application/json; charset=UTF-8')),
+ ('json-compact' , (api_type('compact'), 'application/json; charset=UTF-8')),
+ ('compact' , ('compact', 'text/html; charset=UTF-8')),
+ ('json' , (api_type(), 'application/json; charset=UTF-8')),
+ ('i' , ('compact', 'text/html; charset=UTF-8')),
+ )
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
path = environ['PATH_INFO']
domain_ext = environ.get('reddit-domain-extension')
- for ext, val in self.extensions.iteritems():
+ for ext, val in self.extensions:
if ext == domain_ext or path.endswith('.' + ext):
environ['extension'] = ext
environ['render_style'] = val[0]
View
@@ -33,6 +33,7 @@ def make_map(global_conf={}, app_conf={}):
admin_routes.add(mc)
mc('/login', controller='forms', action='login')
+ mc('/register', controller='forms', action='register')
mc('/logout', controller='forms', action='logout')
mc('/verify', controller='forms', action='verify')
mc('/adminon', controller='forms', action='adminon')
@@ -53,7 +54,7 @@ def make_map(global_conf={}, app_conf={}):
mc('/reddits/create', controller='front', action='newreddit')
mc('/reddits/search', controller='front', action='search_reddits')
- mc('/reddits/login', controller='front', action='login')
+ mc('/reddits/login', controller='forms', action='login')
mc('/reddits/:where', controller='reddits', action='listing',
where = 'popular',
requirements=dict(where="popular|new|banned"))
@@ -72,6 +73,7 @@ def make_map(global_conf={}, app_conf={}):
mc('/widget', controller='buttons', action='widget_demo_page')
mc('/bookmarklets', controller='buttons', action='bookmarklets')
+ mc('/iphonebookmarklet', controller='buttons', action='iphonebookmarklets')
mc('/awards', controller='front', action='awards')
@@ -146,6 +148,7 @@ def make_map(global_conf={}, app_conf={}):
mc('/promoted/', controller='promoted', action = "listing",
sort = "")
+ mc('/health/threads', controller='health', action='threads')
mc('/health', controller='health', action='health')
mc('/shutdown', controller='health', action='shutdown')
@@ -235,6 +238,7 @@ def make_map(global_conf={}, app_conf={}):
mc("/ads/r/:reddit_name", controller = "ad", action = "ad")
mc("/ads/:codename", controller = "ad", action = "ad_by_codename")
+ mc("/try", controller = "forms", action = "try_compact")
mc('/comscore-iframe/', controller='mediaembed', action='comscore')
mc('/comscore-iframe/*url', controller='mediaembed', action='comscore')
@@ -27,6 +27,7 @@
def api(type, cls):
tpm.add_handler(type, 'api', cls())
tpm.add_handler(type, 'api-html', cls())
+ tpm.add_handler(type, 'api-compact', cls())
# blanket fallback rule
api('templated', NullJsonTemplate)
View
@@ -19,7 +19,7 @@
# All portions of the code written by CondeNet are Copyright (c) 2006-2010
# CondeNet, Inc. All Rights Reserved.
################################################################################
-from reddit_base import RedditController, set_user_cookie
+from reddit_base import RedditController, MinimalController, set_user_cookie
from pylons.i18n import _
from pylons import c, request
@@ -34,13 +34,12 @@
from r2.lib.utils import timeago, tup, filter_links
from r2.lib.pages import FriendList, ContributorList, ModList, \
BannedList, BoringPage, FormPage, CssError, UploadedImage, \
- ClickGadget
+ ClickGadget, UrlParser
from r2.lib.utils.trial_utils import indict, end_trial, trial_info
from r2.lib.pages.things import wrap_links, default_thing_wrapper
from r2.lib import spreadshirt
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, websafe, spaceCompress
@@ -71,7 +70,7 @@ def reject_vote(thing):
(voteword, c.user.name, request.ip, thing.__class__.__name__,
thing._id36, request.referer), "info")
-class ApiminimalController(RedditController):
+class ApiminimalController(MinimalController):
"""
Put API calls in here which won't come from logged in users (or
don't rely on the user being logged int)
@@ -197,9 +196,10 @@ def POST_compose(self, form, jquery, to, subject, body, ip):
selftext = VMarkdown('text'),
kind = VOneOf('kind', ['link', 'self', 'poll']),
then = VOneOf('then', ('tb', 'comments'),
- default='comments'))
+ default='comments'),
+ extension = VLength("extension", 20))
def POST_submit(self, form, jquery, url, selftext, kind, title,
- save, sr, ip, then):
+ save, sr, ip, then, extension):
from r2.models.admintools import is_banned_domain
if isinstance(url, (unicode, str)):
@@ -238,7 +238,12 @@ def POST_submit(self, form, jquery, url, selftext, kind, title,
pass
elif form.has_errors("url", errors.ALREADY_SUB):
check_domain = False
- form.redirect(url[0].already_submitted_link)
+ u = url[0].already_submitted_link
+ if extension:
+ u = UrlParser(u)
+ u.set_extension(extension)
+ u = u.unparse()
+ form.redirect(u)
# check for title, otherwise look it up and return it
elif form.has_errors("title", errors.NO_TEXT):
pass
@@ -324,7 +329,8 @@ def POST_submit(self, form, jquery, url, selftext, kind, title,
elif then == 'tb':
form.attr('target', '_top')
path = add_sr('/tb/%s' % l._id36)
-
+ if extension:
+ path += ".%s" % extension
form.redirect(path)
@validatedForm(VRatelimit(rate_ip = True,
@@ -636,7 +642,6 @@ def POST_del(self, thing):
#expire the item from the sr cache
if isinstance(thing, Link):
sr = thing.subreddit_slow
- expire_hot(sr)
queries.delete_links(thing)
#comments have special delete tasks
@@ -1234,6 +1239,23 @@ def POST_site_admin(self, form, jquery, name, ip, sr,
else:
jquery.refresh()
+ @noresponse(q = VPrintable('q', max_length=500),
+ sort = VPrintable('sort', max_length=10),
+ t = VPrintable('t', max_length=10),
+ approval = VBoolean('approval'))
+ def POST_searchfeedback(self, q, sort, t, approval):
+ timestamp = c.start_time.strftime("%Y/%m/%d-%H:%M:%S")
+ if c.user_is_loggedin:
+ username = c.user.name
+ else:
+ username = None
+ d = dict(username=username, q=q, sort=sort, t=t)
+ hex = md5(repr(d)).hexdigest()
+ key = "searchfeedback-%s-%s-%s" % (timestamp[:10], request.ip, hex)
+ d['timestamp'] = timestamp
+ d['approval'] = approval
+ g.hardcache.set(key, d, time=86400 * 7)
+
@noresponse(VUser(), VModhash(),
why = VSrCanBan('id'),
thing = VByName('id'))
@@ -1530,25 +1552,21 @@ def POST_subscribe(self, action, sr):
self._subscribe(sr, action == 'sub')
def _subscribe(self, sr, sub):
- Subreddit.subscribe_defaults(c.user)
-
- sub_key = "subscription-%s-%s" % (c.user.name, sr.name)
+ try:
+ Subreddit.subscribe_defaults(c.user)
- if sub:
- try:
+ if sub:
if sr.add_subscriber(c.user):
sr._incr('_ups', 1)
- except CreationError:
- # This only seems to happen when someone is pounding on the
- # subscribe button or the DBs are really lagged; either way,
- # some other proc has already handled this subscribe request.
- return
- else:
- if sr.remove_subscriber(c.user):
- g.cache.delete(sub_key)
- sr._incr('_ups', -1)
- changed(sr)
-
+ else:
+ if sr.remove_subscriber(c.user):
+ sr._incr('_ups', -1)
+ changed(sr)
+ except CreationError:
+ # This only seems to happen when someone is pounding on the
+ # subscribe button or the DBs are really lagged; either way,
+ # some other proc has already handled this subscribe request.
+ return
@noresponse(VAdmin(),
tr = VTranslation("id"))
Oops, something went wrong.

0 comments on commit 9a4271f

Please sign in to comment.