def GET_wiki_create(self, may_create, page, view=False): api = c.extension == 'json' if c.error and c.error['reason'] == 'PAGE_EXISTS': return self.redirect(join_urls(c.wiki_base_url, page)) elif not may_create or api: if may_create and c.error: self.handle_error(403, **c.error) else: self.handle_error(404, 'PAGE_NOT_FOUND', may_create=may_create) elif c.error: error = '' if c.error['reason'] == 'PAGE_NAME_LENGTH': error = _( "this wiki cannot handle page names of that magnitude! please select a page name shorter than %d characters" ) % c.error['max_length'] elif c.error['reason'] == 'PAGE_CREATED_ELSEWHERE': error = _( "this page is a special page, please go into the subreddit settings and save the field once to create this special page" ) elif c.error['reason'] == 'PAGE_NAME_MAX_SEPARATORS': error = _( 'a max of %d separators "/" are allowed in a wiki page name.' ) % c.error['MAX_SEPARATORS'] return BoringPage(_("Wiki error"), infotext=error).render() elif view: return WikiNotFound().render() elif may_create: WikiPage.create(c.site, page) url = join_urls(c.wiki_base_url, '/edit/', page) return self.redirect(url)
def GET_wiki_create(self, may_create, page, view=False): api = c.extension == 'json' if c.error and c.error['reason'] == 'PAGE_EXISTS': return self.redirect(join_urls(c.wiki_base_url, page)) elif not may_create or api: if may_create and c.error: self.handle_error(403, **c.error) else: self.handle_error(404, 'PAGE_NOT_FOUND', may_create=may_create) elif c.error: error = '' if c.error['reason'] == 'PAGE_NAME_LENGTH': error = _("this wiki cannot handle page names of that magnitude! please select a page name shorter than %d characters") % c.error['max_length'] elif c.error['reason'] == 'PAGE_CREATED_ELSEWHERE': error = _("this page is a special page, please go into the subreddit settings and save the field once to create this special page") elif c.error['reason'] == 'PAGE_NAME_MAX_SEPARATORS': error = _('a max of %d separators "/" are allowed in a wiki page name.') % c.error['MAX_SEPARATORS'] return BoringPage(_("Wiki error"), infotext=error).render() elif view: return WikiNotFound().render() elif may_create: WikiPage.create(c.site, page) url = join_urls(c.wiki_base_url, '/edit/', page) return self.redirect(url)
def may_not_create(self, page): if not page: # Should not happen, but just in case self.set_error('EMPTY_PAGE_NAME', 403) return page = normalize_page(page) if WikiPage.is_automatically_created(page): return {'reason': 'PAGE_CREATED_ELSEWHERE'} elif WikiPage.is_special(page): if not (c.user_is_admin or c.site.is_moderator_with_perms(c.user, 'config')): self.set_error('RESTRICTED_PAGE', code=403) return elif (not c.user_is_admin) and WikiPage.is_restricted(page): self.set_error('RESTRICTED_PAGE', code=403) return elif page.count('/') > MAX_SEPARATORS: return { 'reason': 'PAGE_NAME_MAX_SEPARATORS', 'max_separators': MAX_SEPARATORS } elif len(page) > MAX_PAGE_NAME_LENGTH: return { 'reason': 'PAGE_NAME_LENGTH', 'max_length': MAX_PAGE_NAME_LENGTH }
def post_if_goal_reached(date): # bail out if this day's already been submitted for link in get_recent_name_submissions(): if link.revenue_date == date: return revenue = gold_revenue_multi([date]).get(date, 0) goal = gold_goal_on(date) percent = revenue / float(goal) bucket = int(percent) if bucket == 0: return buyer_count = len(gold_buyers_on(date)) template_wp = WikiPage.get(SERVERNAME_SR, "templates/selftext") template = random.choice(template_wp._get("content").split("\r\n---\r\n")) boilerplate = WikiPage.get(SERVERNAME_SR, "templates/boilerplate")._get("content") selftext_template = template + "\n\n---\n\n" + boilerplate link = Link._submit( is_self=True, title=date.strftime("%a %Y-%m-%d"), content=selftext_template % { "percent": int(percent * 100), "buyers": buyer_count, }, author=SYSTEM_ACCOUNT, sr=SERVERNAME_SR, ip="127.0.0.1", spam=False, ) link.flair_text = "Name pending..." link.flair_css_class = "goal-bucket-%d-active" % bucket link.revenue_date = date link.revenue_bucket = bucket link.server_names = [] link._commit() UPVOTE = True queries.queue_vote(SYSTEM_ACCOUNT, link, UPVOTE, "127.0.0.1") queries.new_link(link) link.update_search_index() template = WikiPage.get(SERVERNAME_SR, "templates/notification-message")._get("content") subject_template, sep, body_template = template.partition("\r\n") for id in gold_buyers_on(date): recipient = Account._byID(id, data=True) send_system_message( recipient, subject_template, body_template % { "percent": int(percent * 100), "buyers": buyer_count, "user": recipient.name, "link": link.url, }, )
def post_if_goal_reached(date): # bail out if this day's already been submitted for link in get_recent_name_submissions(): if link.revenue_date == date: return revenue = gold_revenue_multi([date]).get(date, 0) goal = gold_goal_on(date) percent = revenue / float(goal) bucket = int(percent) if bucket == 0: return buyer_count = len(gold_buyers_on(date)) template_wp = WikiPage.get(SERVERNAME_SR, "templates/selftext") template = random.choice(template_wp._get("content").split("\r\n---\r\n")) boilerplate = WikiPage.get(SERVERNAME_SR, "templates/boilerplate")._get("content") selftext_template = template + "\n\n---\n\n" + boilerplate link = Link._submit( is_self=True, title=date.strftime("%a %Y-%m-%d"), content=selftext_template % { "percent": int(percent * 100), "buyers": buyer_count, }, author=SYSTEM_ACCOUNT, sr=SERVERNAME_SR, ip="127.0.0.1", spam=False, ) link.flair_text = "Name pending..." link.flair_css_class = "goal-bucket-%d-active" % bucket link.revenue_date = date link.revenue_bucket = bucket link.server_names = [] link._commit() queries.new_link(link) link.update_search_index() template = WikiPage.get(SERVERNAME_SR, "templates/notification-message")._get("content") subject_template, sep, body_template = template.partition("\r\n") for id in gold_buyers_on(date): recipient = Account._byID(id, data=True) send_system_message( recipient, subject_template, body_template % { "percent": int(percent * 100), "buyers": buyer_count, "user": recipient.name, "link": link.url, }, )
def may_not_create(self, page): if c.is_wiki_mod and WikiPage.is_special(page): return {'reason': 'PAGE_CREATED_ELSEWHERE'} elif (not c.user_is_admin) and WikiPage.is_restricted(page): self.set_error('RESTRICTED_PAGE', code=403) return elif page.count('/') > MAX_SEPARATORS: return {'reason': 'PAGE_NAME_MAX_SEPARATORS', 'max_separators': MAX_SEPARATORS} elif len(page) > MAX_PAGE_NAME_LENGTH: return {'reason': 'PAGE_NAME_LENGTH', 'max_length': MAX_PAGE_NAME_LENGTH}
def change_css(self, content, parsed, prev=None, reason=None, author=None, force=False): from r2.models import ModAction author = author if author else c.user.name if content is None: content = '' try: wiki = WikiPage.get(self, 'config/stylesheet') except tdb_cassandra.NotFound: wiki = WikiPage.create(self, 'config/stylesheet') wr = wiki.revise(content, previous=prev, author=author, reason=reason, force=force) minified = cssmin(parsed) if minified: if g.static_stylesheet_bucket: digest = hashlib.sha1(minified).digest() self.stylesheet_hash = ( base64.urlsafe_b64encode(digest).rstrip("=")) s3cp.send_file( g.static_stylesheet_bucket, self.static_stylesheet_name, minified, content_type="text/css", never_expire=True, replace=False, ) self.stylesheet_contents = "" self.stylesheet_modified = None else: self.stylesheet_hash = hashlib.md5(minified).hexdigest() self.stylesheet_contents = minified self.stylesheet_modified = datetime.datetime.now(g.tz) else: self.stylesheet_contents = "" self.stylesheet_hash = "" self.stylesheet_modified = datetime.datetime.now(g.tz) self.stylesheet_contents_user = "" # reads from wiki; ensure pg clean self._commit() ModAction.create(self, c.user, action='wikirevise', details='Updated subreddit stylesheet') return wr
def activate_names(link, names): for comment, name in names: # find a slot to assign a name to. we'll prefer nodes that are # currently empty, and failing that find the least-recently-modified # node. ROOT = "/gold/server-names" slot_names = g.zookeeper.get_children(ROOT) slots = [(slot_name, g.zookeeper.get(os.path.join(ROOT, slot_name))) for slot_name in slot_names] slots.sort(key=lambda (path, (data, stat)): (bool(data), stat.mtime)) slot_path = os.path.join(ROOT, slots[0][0]) comment_data = { 'name': str(name), 'permalink': comment.make_permalink_slow() } g.zookeeper.set(slot_path, json.dumps(comment_data)) lock = g.zookeeper.Lock(slot_path) lock_contenders = lock.contenders() old_name = lock_contenders[0] if lock_contenders else "" old_name = old_name or "one of our servers" # reply to the user wp = WikiPage.get(SERVERNAME_SR, "templates/success-reply") template = random.choice(wp._get("content").split("\r\n---\r\n")) comment, inbox_rel = Comment._new( author=SYSTEM_ACCOUNT, link=link, parent=comment, body=template % { "old-name": old_name, "new-name": name, }, ip="127.0.0.1", ) queries.new_comment(comment, inbox_rel) # update the link's text wp = WikiPage.get(SERVERNAME_SR, "templates/goldisms") goldism = random.choice(wp._get("content").split("\r\n---\r\n")) wp = WikiPage.get(SERVERNAME_SR, "templates/selftext-success") template = wp._get("content") link.selftext = template % { "old-name": old_name, "new-name": name, "goldism": goldism, } link._commit()
def activate_names(link, names): for comment, name in names: # find a slot to assign a name to. we'll prefer nodes that are # currently empty, and failing that find the least-recently-modified # node. ROOT = "/gold/server-names" slot_names = g.zookeeper.get_children(ROOT) slots = [(slot_name, g.zookeeper.get(os.path.join(ROOT, slot_name))) for slot_name in slot_names] slots.sort(key=lambda (path, (data, stat)): (bool(data), stat.mtime)) slot_path = os.path.join(ROOT, slots[0][0]) comment_data = {'name': str(name), 'permalink': comment.make_permalink_slow()} g.zookeeper.set(slot_path, json.dumps(comment_data)) lock = g.zookeeper.Lock(slot_path) lock_contenders = lock.contenders() old_name = lock_contenders[0] if lock_contenders else "" old_name = old_name or "one of our servers" # reply to the user wp = WikiPage.get(SERVERNAME_SR, "templates/success-reply") template = random.choice(wp._get("content").split("\r\n---\r\n")) comment, inbox_rel = Comment._new( author=SYSTEM_ACCOUNT, link=link, parent=comment, body=template % { "old-name": old_name, "new-name": name, }, ip="127.0.0.1", ) queries.queue_vote(SYSTEM_ACCOUNT, comment, dir=True, ip="127.0.0.1") queries.new_comment(comment, inbox_rel) # update the link's text wp = WikiPage.get(SERVERNAME_SR, "templates/goldisms") goldism = random.choice(wp._get("content").split("\r\n---\r\n")) wp = WikiPage.get(SERVERNAME_SR, "templates/selftext-success") template = wp._get("content") link.selftext = template % { "old-name": old_name, "new-name": name, "goldism": goldism, } link._commit()
def GET_wiki_listing(self): def check_hidden(page): g.log.debug("Got here %s" % str(this_may_view(page))) return this_may_view(page) pages = WikiPage.get_listing(c.site, filter_check=check_hidden) return WikiListing(pages).render()
def may_view(sr, user, page): # User being None means not logged in mod = sr.is_moderator_with_perms(user, 'wiki') if user else False if mod: # Mods may always view return True if page.special: level = WikiPage.get_special_view_permlevel(page.name) else: level = page.permlevel if level < 2: # Everyone may view in levels below 2 return True if level == 2: # Only mods may view in level 2 return mod # In any other obscure level, # (This should not happen but just in case) # nobody may view. return False
def run(self, page): original_page = page if not page: # If no page is specified, give the index page page = "index" try: page = str(page) except UnicodeEncodeError: return self.set_error('INVALID_PAGE_NAME', code=400) if ' ' in page: page = page.replace(' ', '_') if not page_match_regex.match(page): return self.set_error('INVALID_PAGE_NAME', code=400) page = normalize_page(page) if WikiPage.is_impossible(page): return self.set_error('INVALID_PAGE_NAME', code=400) if self.error_on_name_normalized and page != original_page: self.set_error('PAGE_NAME_NORMALIZED') return page
def change_css(self, content, parsed, prev=None, reason=None, author=None, force=False): from r2.models import ModAction author = author if author else c.user.name if content is None: content = '' try: wiki = WikiPage.get(self, 'config/stylesheet') except tdb_cassandra.NotFound: wiki = WikiPage.create(self, 'config/stylesheet') wr = wiki.revise(content, previous=prev, author=author, reason=reason, force=force) self.stylesheet_contents = parsed self.stylesheet_hash = md5(parsed).hexdigest() set_last_modified(self, 'stylesheet_contents') c.site._commit() ModAction.create(self, c.user, action='wikirevise', details='Updated subreddit stylesheet') return wr
def run(self, page): wp = VWikiPage.run(self, page) if c.errors: return if wp: c.error = {"reason": "PAGE_EXISTS"} elif c.is_wiki_mod and WikiPage.is_special(page): c.error = {"reason": "PAGE_CREATED_ELSEWHERE"} elif WikiPage.is_restricted(page): self.set_error("RESTRICTED_PAGE", code=403) return elif page.count("/") > MAX_SEPARATORS: c.error = {"reason": "PAGE_NAME_MAX_SEPARATORS", "MAX_SEPARATORS": MAX_SEPARATORS} elif len(page) > MAX_PAGE_NAME_LENGTH: c.error = {"reason": "PAGE_NAME_LENGTH", "max_length": MAX_PAGE_NAME_LENGTH} return this_may_revise()
def POST_wiki_edit(self, pageandprevious, content, page_name, reason): """Edit a wiki `page`""" page, previous = pageandprevious if not page: error = c.errors.get(("WIKI_CREATE_ERROR", "page")) if error: self.handle_error(403, **(error.msg_params or {})) if not c.user._spam: page = WikiPage.create(c.site, page_name) if c.user._spam: error = _("You are doing that too much, please try again later.") self.handle_error(415, "SPECIAL_ERRORS", special_errors=[error]) renderer = RENDERERS_BY_PAGE.get(page.name, "wiki") if renderer in ("wiki", "reddit"): content = VMarkdown(("content"), renderer=renderer).run(content) # Use the raw POST value as we need to tell the difference between # None/Undefined and an empty string. The validators use a default # value with both of those cases and would need to be changed. # In order to avoid breaking functionality, this was done instead. previous = previous._id if previous else request.POST.get("previous") try: # special validation methods if page.name == "config/stylesheet": css_errors, parsed = c.site.parse_css(content, verify=False) if g.css_killswitch: self.handle_error(403, "STYLESHEET_EDIT_DENIED") if css_errors: error_items = [CssError(x).message for x in css_errors] self.handle_error(415, "SPECIAL_ERRORS", special_errors=error_items) elif page.name == "config/automoderator": try: rules = Ruleset(content) except ValueError as e: error_items = [e.message] self.handle_error(415, "SPECIAL_ERRORS", special_errors=error_items) # special saving methods if page.name == "config/stylesheet": c.site.change_css(content, parsed, previous, reason=reason) else: try: page.revise(content, previous, c.user._id36, reason=reason) except ContentLengthError as e: self.handle_error(403, "CONTENT_LENGTH_ERROR", max_length=e.max_length) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special and page.name in ATTRIBUTE_BY_PAGE: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) c.site._commit() if page.special or c.is_wiki_mod: description = modactions.get(page.name, "Page %s edited" % page.name) ModAction.create(c.site, c.user, "wikirevise", details=description) except ConflictException as e: self.handle_error(409, "EDIT_CONFLICT", newcontent=e.new, newrevision=page.revision, diffcontent=e.htmldiff) return json.dumps({})
def GET_policy_page(self, page, requested_rev): if c.render_style == 'compact': self.redirect('/wiki/' + page) if page == 'privacypolicy': wiki_name = g.wiki_page_privacy_policy pagename = _('privacy policy') elif page == 'useragreement': wiki_name = g.wiki_page_user_agreement pagename = _('user agreement') elif page == 'contentpolicy': wiki_name = g.wiki_page_content_policy pagename = _('content policy') else: abort(404) wp = WikiPage.get(Frontpage, wiki_name) revs = list(wp.get_revisions()) # collapse minor edits into revisions with reasons rev_info = [] last_edit = None for rev in revs: if rev.is_hidden: continue if not last_edit: last_edit = rev if rev._get('reason'): rev_info.append({ 'id': str(last_edit._id), 'title': rev._get('reason'), }) last_edit = None if requested_rev: try: display_rev = WikiRevision.get(requested_rev, wp._id) except (tdb_cassandra.NotFound, WikiBadRevision): abort(404) else: display_rev = revs[0] doc_html = wikimarkdown(display_rev.content, include_toc=False) soup = BeautifulSoup(doc_html.decode('utf-8')) toc = generate_table_of_contents(soup, prefix='section') self._number_sections(soup) self._linkify_headings(soup) content = PolicyView( body_html=unsafe(soup), toc_html=unsafe(toc), revs=rev_info, display_rev=str(display_rev._id), ) return PolicyPage( pagename=pagename, content=content, ).render()
def GET_wiki_listing(self): """Retrieve a list of wiki pages in this subreddit""" def check_hidden(page): return page.listed and this_may_view(page) pages, linear_pages = WikiPage.get_listing(c.site, filter_check=check_hidden) return WikiListing(pages, linear_pages).render()
def may_not_create(self, page): if not page: # Should not happen, but just in case self.set_error("EMPTY_PAGE_NAME", 403) return page = normalize_page(page) if c.is_wiki_mod and WikiPage.is_special(page): return {"reason": "PAGE_CREATED_ELSEWHERE"} elif (not c.user_is_admin) and WikiPage.is_restricted(page): self.set_error("RESTRICTED_PAGE", code=403) return elif page.count("/") > MAX_SEPARATORS: return {"reason": "PAGE_NAME_MAX_SEPARATORS", "max_separators": MAX_SEPARATORS} elif len(page) > MAX_PAGE_NAME_LENGTH: return {"reason": "PAGE_NAME_LENGTH", "max_length": MAX_PAGE_NAME_LENGTH}
def change_css(self, content, parsed, prev=None, reason=None, author=None, force=False): from r2.models import ModAction author = author if author else c.user.name if content is None: content = "" try: wiki = WikiPage.get(self, "config/stylesheet") except tdb_cassandra.NotFound: wiki = WikiPage.create(self, "config/stylesheet") wr = wiki.revise(content, previous=prev, author=author, reason=reason, force=force) minified = cssmin(parsed) if minified: if g.static_stylesheet_bucket: digest = hashlib.sha1(minified).digest() self.stylesheet_hash = base64.urlsafe_b64encode(digest).rstrip("=") s3cp.send_file( g.static_stylesheet_bucket, self.static_stylesheet_name, minified, content_type="text/css", never_expire=True, replace=False, ) self.stylesheet_contents = "" self.stylesheet_modified = None else: self.stylesheet_hash = hashlib.md5(minified).hexdigest() self.stylesheet_contents = minified self.stylesheet_modified = datetime.datetime.now(g.tz) else: self.stylesheet_contents = "" self.stylesheet_hash = "" self.stylesheet_modified = datetime.datetime.now(g.tz) self.stylesheet_contents_user = "" # reads from wiki; ensure pg clean self._commit() ModAction.create(self, c.user, action="wikirevise", details="Updated subreddit stylesheet") return wr
def wiki_template(template_slug, sr=None): """Pull content from a subreddit's wiki page for internal use.""" if not sr: sr = Subreddit._by_name(g.default_sr) try: wiki = WikiPage.get(sr, "templates/%s" % template_slug) except tdb_cassandra.NotFound: return None return wiki._get("content")
def may_not_create(self, page): if not page: # Should not happen, but just in case self.set_error('EMPTY_PAGE_NAME', 403) return page = normalize_page(page) if WikiPage.is_automatically_created(page): return {'reason': 'PAGE_CREATED_ELSEWHERE'} elif WikiPage.is_special(page): if not (c.user_is_admin or c.site.is_moderator_with_perms(c.user, 'config')): self.set_error('RESTRICTED_PAGE', code=403) return elif (not c.user_is_admin) and WikiPage.is_restricted(page): self.set_error('RESTRICTED_PAGE', code=403) return elif page.count('/') > MAX_SEPARATORS: return {'reason': 'PAGE_NAME_MAX_SEPARATORS', 'max_separators': MAX_SEPARATORS} elif len(page) > MAX_PAGE_NAME_LENGTH: return {'reason': 'PAGE_NAME_LENGTH', 'max_length': MAX_PAGE_NAME_LENGTH}
def run(self, page): wp = VWikiPage.run(self, page) if c.errors: return if wp: c.error = {'reason': 'PAGE_EXISTS'} elif c.is_wiki_mod and WikiPage.is_special(page): c.error = {'reason': 'PAGE_CREATED_ELSEWHERE'} elif WikiPage.is_restricted(page): self.set_error('RESTRICTED_PAGE', code=403) return elif page.count('/') > MAX_SEPARATORS: c.error = { 'reason': 'PAGE_NAME_MAX_SEPARATORS', 'MAX_SEPARATORS': MAX_SEPARATORS } elif len(page) > MAX_PAGE_NAME_LENGTH: c.error = { 'reason': 'PAGE_NAME_LENGTH', 'max_length': MAX_PAGE_NAME_LENGTH } return this_may_revise()
def POST_wiki_edit(self, pageandprevious, content, page_name, reason): """Edit a wiki `page`""" page, previous = pageandprevious if not page: error = c.errors.get(('WIKI_CREATE_ERROR', 'page')) if error: self.handle_error(403, **(error.msg_params or {})) if not c.user._spam: page = WikiPage.create(c.site, page_name) if c.user._spam: error = _("You are doing that too much, please try again later.") self.handle_error(415, 'SPECIAL_ERRORS', special_errors=[error]) renderer = RENDERERS_BY_PAGE.get(page.name, 'wiki') if renderer in ('wiki', 'reddit'): content = VMarkdown(('content'), renderer=renderer).run(content) # Use the raw POST value as we need to tell the difference between # None/Undefined and an empty string. The validators use a default # value with both of those cases and would need to be changed. # In order to avoid breaking functionality, this was done instead. previous = previous._id if previous else request.POST.get('previous') try: if page.name == 'config/stylesheet': report, parsed = c.site.parse_css(content, verify=False) if report is None: # g.css_killswitch self.handle_error(403, 'STYLESHEET_EDIT_DENIED') if report.errors: error_items = [x.message for x in sorted(report.errors)] self.handle_error(415, 'SPECIAL_ERRORS', special_errors=error_items) c.site.change_css(content, parsed, previous, reason=reason) else: try: page.revise(content, previous, c.user._id36, reason=reason) except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", page.revision) c.site._commit() if page.special or c.is_wiki_mod: description = modactions.get(page.name, 'Page %s edited' % page.name) ModAction.create(c.site, c.user, 'wikirevise', details=description) except ConflictException as e: self.handle_error(409, 'EDIT_CONFLICT', newcontent=e.new, newrevision=page.revision, diffcontent=e.htmldiff) return json.dumps({})
def POST_wiki_edit(self, pageandprevious, content, page_name, reason): page, previous = pageandprevious if not page: error = c.errors.get(('WIKI_CREATE_ERROR', 'page')) if error: self.handle_error(403, **(error.msg_params or {})) if not c.user._spam: page = WikiPage.create(c.site, page_name) if c.user._spam: error = _("You are doing that too much, please try again later.") self.handle_error(415, 'SPECIAL_ERRORS', special_errors=[error]) renderer = RENDERERS_BY_PAGE.get(page.name, 'wiki') if renderer in ('wiki', 'reddit'): content = VMarkdown(('content'), renderer=renderer).run(content) # Use the raw POST value as we need to tell the difference between # None/Undefined and an empty string. The validators use a default # value with both of those cases and would need to be changed. # In order to avoid breaking functionality, this was done instead. previous = previous._id if previous else request.post.get('previous') try: if page.name == 'config/stylesheet': report, parsed = c.site.parse_css(content, verify=False) if report is None: # g.css_killswitch self.handle_error(403, 'STYLESHEET_EDIT_DENIED') if report.errors: error_items = [x.message for x in sorted(report.errors)] self.handle_error(415, 'SPECIAL_ERRORS', special_errors=error_items) c.site.change_css(content, parsed, previous, reason=reason) else: try: page.revise(content, previous, c.user._id36, reason=reason) except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) setattr(c.site, "prev_" + ATTRIBUTE_BY_PAGE[page.name] + "_id", str(page.revision)) c.site._commit() if page.special or c.is_wiki_mod: description = modactions.get(page.name, 'Page %s edited' % page.name) ModAction.create(c.site, c.user, 'wikirevise', details=description) except ConflictException as e: self.handle_error(409, 'EDIT_CONFLICT', newcontent=e.new, newrevision=page.revision, diffcontent=e.htmldiff) return json.dumps({})
def validpage(self, page): try: wp = WikiPage.get(c.site, page) if self.restricted and wp.restricted: if not (c.is_wiki_mod or wp.special): self.set_error('RESTRICTED_PAGE', code=403) raise AbortWikiError if not this_may_view(wp): self.set_error('MAY_NOT_VIEW', code=403) raise AbortWikiError return wp except tdb_cassandra.NotFound: if self.required: self.set_error('PAGE_NOT_FOUND', code=404) raise AbortWikiError return None
def wiki_template(template_slug, sr=None): """Pull content from a subreddit's wiki page for internal use.""" if not sr: try: sr = Subreddit._by_name(g.default_sr) except NotFound: # a freshly-build dev instance with no pre-populated data # will die on account creation (and other user actions) if a # default is not set. Guard against that for testing. if g.debug: return raise try: wiki = WikiPage.get(sr, "templates/%s" % template_slug) except tdb_cassandra.NotFound: return None return wiki._get("content")
def append_random_bottlecap_phrase(message): """Appends a random "bottlecap" phrase from the wiki page. The wiki page should be an unordered list with each item a separate bottlecap. """ bottlecap = None try: wp = WikiPage.get(Frontpage, g.wiki_page_gold_bottlecaps) split_list = re.split('^[*-] ', wp.content, flags=re.MULTILINE) choices = [item.strip() for item in split_list if item.strip()] if len(choices): bottlecap = choice(choices) except NotFound: pass if bottlecap: message += '\n\n> ' + bottlecap return message
def run(self, page): original_page = page try: page = str(page) if page else "" except UnicodeEncodeError: return self.set_error('INVALID_PAGE_NAME', code=400) page = normalize_page(page) if page and not page_match_regex.match(page): return self.set_error('INVALID_PAGE_NAME', code=400) # If no page is specified, give the index page page = page or "index" if WikiPage.is_impossible(page): return self.set_error('INVALID_PAGE_NAME', code=400) if self.error_on_name_normalized and page != original_page: self.set_error('PAGE_NAME_NORMALIZED') return page
def run(self, page): original_page = page try: page = str(page) if page else "" except UnicodeEncodeError: return self.set_error("INVALID_PAGE_NAME", code=400) page = normalize_page(page) if page and not page_match_regex.match(page): return self.set_error("INVALID_PAGE_NAME", code=400) # If no page is specified, give the index page page = page or "index" if WikiPage.is_impossible(page): return self.set_error("INVALID_PAGE_NAME", code=400) if self.error_on_name_normalized and page != original_page: self.set_error("PAGE_NAME_NORMALIZED") return page
def run(self, page): self.perms = ['wiki'] if page and WikiPage.is_special(page): self.perms += ['config'] VSrModerator.run(self)
def GET_wiki_listing(self): def check_hidden(page): return this_may_view(page) pages, linear_pages = WikiPage.get_listing(c.site, filter_check=check_hidden) return WikiListing(pages, linear_pages).render()
def prev_stylesheet(self): try: return WikiPage.get(self, 'config/stylesheet')._get('revision', '') except tdb_cassandra.NotFound: return ''
def stylesheet_contents_user(self): try: return WikiPage.get(self, 'config/stylesheet')._get('content', '') except tdb_cassandra.NotFound: return self._t.get('stylesheet_contents_user')
def public_description(self): try: return WikiPage.get(self, 'config/description')._get('content','') except tdb_cassandra.NotFound: return self._t.get('public_description')
def prev_stylesheet(self): try: return WikiPage.get(self, 'config/stylesheet')._get('revision','') except tdb_cassandra.NotFound: return ''
def prev_stylesheet(self): try: return WikiPage.get(self, "config/stylesheet")._get("revision", "") except tdb_cassandra.NotFound: return ""
from r2.lib.media import upload_media from r2.models.subreddit import Subreddit from r2.models.wiki import WikiPage, ImagesByWikiPage all_subreddits = Subreddit._query(sort=desc("_date")) for sr in fetch_things2(all_subreddits): images = sr.images.copy() images.pop("/empties/", None) if not images: continue print 'Processing /r/%s (id36: %s)' % (sr.name, sr._id36) # upgrade old-style image ids to urls for name, image_url in images.items(): if not isinstance(image_url, int): continue print " upgrading image %r" % image_url url = "http://%s/%s_%d.png" % (g.s3_old_thumb_bucket, sr._fullname, image_url) image_data = urllib2.urlopen(url).read() new_url = upload_media(image_data, file_type=".png") images[name] = new_url # use a timestamp of zero to make sure that we don't overwrite any changes # from live dual-writes. rowkey = WikiPage.id_for(sr, "config/stylesheet") ImagesByWikiPage._cf.insert(rowkey, images, timestamp=0)
def stylesheet_contents_user(self): try: return WikiPage.get(self, 'config/stylesheet')._get('content','') except tdb_cassandra.NotFound: return self._t.get('stylesheet_contents_user')
def prev_public_description_id(self): try: return WikiPage.get(self, 'config/description')._get('revision','') except tdb_cassandra.NotFound: return ''
def stylesheet_contents_user(self): try: return WikiPage.get(self, "config/stylesheet")._get("content", "") except tdb_cassandra.NotFound: return self._t.get("stylesheet_contents_user")
from r2.models.subreddit import Subreddit from r2.models.wiki import WikiPage, ImagesByWikiPage all_subreddits = Subreddit._query(sort=desc("_date")) for sr in fetch_things2(all_subreddits): images = sr.images.copy() images.pop("/empties/", None) if not images: continue print 'Processing /r/%s (id36: %s)' % (sr.name, sr._id36) # upgrade old-style image ids to urls for name, image_url in images.items(): if not isinstance(image_url, int): continue print " upgrading image %r" % image_url url = "http://%s/%s_%d.png" % (g.s3_old_thumb_bucket, sr._fullname, image_url) image_data = urllib2.urlopen(url).read() new_url = upload_media(image_data, file_type=".png") images[name] = new_url # use a timestamp of zero to make sure that we don't overwrite any changes # from live dual-writes. rowkey = WikiPage.id_for(sr, "config/stylesheet") ImagesByWikiPage._cf.insert(rowkey, images, timestamp=0)
def POST_wiki_edit(self, pageandprevious, content, page_name, reason): """Edit a wiki `page`""" page, previous = pageandprevious if c.user._spam: error = _("You are doing that too much, please try again later.") self.handle_error(415, 'SPECIAL_ERRORS', special_errors=[error]) if not page: error = c.errors.get(('WIKI_CREATE_ERROR', 'page')) if error: self.handle_error(403, **(error.msg_params or {})) VNotInTimeout().run(action_name="wikirevise", details_text="create") page = WikiPage.create(c.site, page_name) else: VNotInTimeout().run(action_name="wikirevise", details_text="edit", target=page) error = c.errors.get(('MAY_NOT_REVISE', 'page')) if error: self.handle_error(403, **(error.msg_params or {})) renderer = RENDERERS_BY_PAGE.get(page.name, 'wiki') if renderer in ('wiki', 'reddit'): content = VMarkdown(('content'), renderer=renderer).run(content) # Use the raw POST value as we need to tell the difference between # None/Undefined and an empty string. The validators use a default # value with both of those cases and would need to be changed. # In order to avoid breaking functionality, this was done instead. previous = previous._id if previous else request.POST.get('previous') try: # special validation methods if page.name == 'config/stylesheet': css_errors, parsed = c.site.parse_css(content, verify=False) if g.css_killswitch: self.handle_error(403, 'STYLESHEET_EDIT_DENIED') if css_errors: error_items = [CssError(x).message for x in css_errors] self.handle_error(415, 'SPECIAL_ERRORS', special_errors=error_items) elif page.name == "config/automoderator": try: rules = Ruleset(content) except ValueError as e: error_items = [e.message] self.handle_error(415, "SPECIAL_ERRORS", special_errors=error_items) # special saving methods if page.name == "config/stylesheet": c.site.change_css(content, parsed, previous, reason=reason) else: try: page.revise(content, previous, c.user._id36, reason=reason) except ContentLengthError as e: self.handle_error(403, 'CONTENT_LENGTH_ERROR', max_length=e.max_length) # continue storing the special pages as data attributes on the subreddit # object. TODO: change this to minimize subreddit get sizes. if page.special and page.name in ATTRIBUTE_BY_PAGE: setattr(c.site, ATTRIBUTE_BY_PAGE[page.name], content) c.site._commit() if page.special or c.is_wiki_mod: description = modactions.get(page.name, 'Page %s edited' % page.name) ModAction.create(c.site, c.user, "wikirevise", details=description, description=reason) except ConflictException as e: self.handle_error(409, 'EDIT_CONFLICT', newcontent=e.new, newrevision=page.revision, diffcontent=e.htmldiff) return json.dumps({})
def sr_admin_menu(self): buttons = [] is_single_subreddit = not isinstance(c.site, (ModSR, MultiReddit)) is_admin = c.user_is_loggedin and c.user_is_admin is_moderator_with_perms = lambda *perms: ( is_admin or c.site.is_moderator_with_perms(c.user, *perms)) if is_single_subreddit and is_moderator_with_perms('config'): buttons.append(NavButton(menu.community_settings, css_class="reddit-edit", dest="edit")) buttons.append(NavButton(menu.edit_stylesheet, css_class="edit-stylesheet", dest="stylesheet")) if is_moderator_with_perms('mail'): buttons.append(NamedButton("modmail", dest="message/inbox", css_class="moderator-mail")) if is_single_subreddit: if is_moderator_with_perms('access'): buttons.append(NamedButton("moderators", css_class="reddit-moderators")) if not c.site.hide_contributors: buttons.append(NavButton( menu.contributors, "contributors", css_class="reddit-contributors")) buttons.append(NamedButton("traffic", css_class="reddit-traffic")) if is_moderator_with_perms('posts'): buttons += [NamedButton("modqueue", css_class="reddit-modqueue"), NamedButton("reports", css_class="reddit-reported"), NamedButton("spam", css_class="reddit-spam"), NamedButton("edited", css_class="reddit-edited")] if is_single_subreddit: if is_moderator_with_perms('access'): buttons.append(NamedButton("banned", css_class="reddit-ban")) if is_moderator_with_perms('flair'): buttons.append(NamedButton("flair", css_class="reddit-flair")) if is_single_subreddit and is_moderator_with_perms('wiki'): # append automod button if they have an AutoMod configuration try: WikiPage.get(c.site, "config/automoderator") buttons.append(NamedButton( "automod", dest="../wiki/config/automoderator", css_class="reddit-automod", )) except tdb_cassandra.NotFound: pass buttons.append(NamedButton("log", css_class="reddit-moderationlog")) if is_moderator_with_perms('posts'): buttons.append( NamedButton("unmoderated", css_class="reddit-unmoderated")) return SideContentBox(_('moderation tools'), [NavMenu(buttons, type="flat_vert", base_path="/about/", css_class="icon-menu", separator="")], _id="moderation_tools", collapsible=True)