From 2de837f52a1b813ab460619a7d1010927a69d362 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 6 May 2009 15:39:16 -0700 Subject: [PATCH] New help system and footer --- r2/example.ini | 2 + r2/r2/controllers/embed.py | 63 +++++++++---- r2/r2/controllers/front.py | 3 +- r2/r2/lib/app_globals.py | 1 + r2/r2/lib/base.py | 34 ++----- r2/r2/lib/c/filters.c | 141 ++++++++++------------------- r2/r2/lib/filters.py | 21 +++-- r2/r2/lib/menus.py | 18 ++-- r2/r2/lib/pages/pages.py | 81 ++++++++++++++--- r2/r2/models/account.py | 8 ++ r2/r2/public/static/css/reddit.css | 72 +++++++++------ r2/r2/templates/helppage.html | 30 ++++++ r2/r2/templates/navmenu.html | 11 ++- r2/r2/templates/pagenamenav.html | 6 ++ r2/r2/templates/redditfooter.html | 33 ++++--- 15 files changed, 313 insertions(+), 211 deletions(-) create mode 100644 r2/r2/templates/helppage.html diff --git a/r2/example.ini b/r2/example.ini index c9fd46796..73c011969 100644 --- a/r2/example.ini +++ b/r2/example.ini @@ -115,6 +115,8 @@ MIN_DOWN_KARMA = 0 MIN_RATE_LIMIT_KARMA = 0 MIN_RATE_LIMIT_COMMENT_KARMA = 0 +WIKI_KARMA = 50 + # time in days MODWINDOW = 2 HOT_PAGE_AGE = 1 diff --git a/r2/r2/controllers/embed.py b/r2/r2/controllers/embed.py index 246a71186..f87908d2b 100644 --- a/r2/r2/controllers/embed.py +++ b/r2/r2/controllers/embed.py @@ -20,12 +20,15 @@ # CondeNet, Inc. All Rights Reserved. ################################################################################ from reddit_base import RedditController, proxyurl -from pylons import request -from r2.lib.pages import Embed, BoringPage +from r2.lib.pages import Embed, BoringPage, HelpPage +from r2.lib.filters import websafe, SC_OFF, SC_ON from pylons.i18n import _ -from urllib2 import HTTPError +from pylons import request from pylons import c +from BeautifulSoup import BeautifulSoup, Tag + +from urllib2 import HTTPError def force_redirect(dest): def _force_redirect(self, *a, **kw): @@ -33,27 +36,51 @@ def force_redirect(dest): return _force_redirect class EmbedController(RedditController): + def rendercontent(self, input, fp): + soup = BeautifulSoup(input) - def rendercontent(self, content): - if content.startswith(""): - return BoringPage(_("help"), - content = Embed(content=content), - show_sidebar = None, - space_compress = False).render() - else: - return content + output = soup.find("div", { 'class':'wiki', 'id':'content'} ) + + # Replace all links to "/wiki/help/..." with "/help/..." + for link in output.findAll('a'): + if link.has_key('href') and link['href'].startswith("/wiki/help"): + link['href'] = link['href'][5:] + + # Add "edit this page" link if the user is allowed to edit the wiki + if c.user_is_loggedin and c.user.can_wiki(): + edit_text = _('edit this page') + read_first = _('read this first') + url = "http://code.reddit.com/wiki" + websafe(fp) + "?action=edit" + + edittag = """ + + """ % (url, edit_text, read_first) + + output.append(edittag) + + output = SC_OFF + unicode(output) + SC_ON + + return HelpPage(_("help"), + content = Embed(content=output), + show_sidebar = None).render() def renderurl(self): + + # Needed so http://reddit.com/help/ works + fp = request.path.rstrip("/") + u = "http://code.reddit.com/wiki" + fp + '?stripped=1' + try: - content = proxyurl("http://reddit.infogami.com"+request.fullpath) - return self.rendercontent(content) + content = proxyurl(u) + return self.rendercontent(content, fp) except HTTPError, e: - if e.code == 404: - return self.abort404() - else: + if e.code != 404: print "error %s" % e.code print e.fp.read() + return self.abort404() GET_help = POST_help = renderurl - - GET_blog = force_redirect("http://blog.reddit.com/") diff --git a/r2/r2/controllers/front.py b/r2/r2/controllers/front.py index 4abd05c0f..4afe9f0fb 100644 --- a/r2/r2/controllers/front.py +++ b/r2/r2/controllers/front.py @@ -465,7 +465,8 @@ class FrontController(RedditController): returns their user name""" c.response_content_type = 'text/plain' if c.user_is_loggedin: - c.response.content = c.user.name + perm = str(c.user.can_wiki()) + c.response.content = c.user.name + "," + perm else: c.response.content = '' return c.response diff --git a/r2/r2/lib/app_globals.py b/r2/r2/lib/app_globals.py index 91e7f3837..86b850d80 100644 --- a/r2/r2/lib/app_globals.py +++ b/r2/r2/lib/app_globals.py @@ -38,6 +38,7 @@ class Globals(object): 'MIN_DOWN_KARMA', 'MIN_RATE_LIMIT_KARMA', 'MIN_RATE_LIMIT_COMMENT_KARMA', + 'WIKI_KARMA', 'HOT_PAGE_AGE', 'MODWINDOW', 'RATELIMIT', diff --git a/r2/r2/lib/base.py b/r2/r2/lib/base.py index bf157c14e..2852585de 100644 --- a/r2/r2/lib/base.py +++ b/r2/r2/lib/base.py @@ -26,10 +26,13 @@ from pylons.i18n import N_, _, ungettext, get_lang import r2.lib.helpers as h from r2.lib.utils import to_js from r2.lib.filters import spaceCompress, _force_unicode +from r2.lib.template_helpers import get_domain from utils import storify, string2js, read_http_date import re, md5 from urllib import quote +import urllib2 + #TODO hack import logging @@ -159,21 +162,14 @@ class BaseController(WSGIController): c.response.content = to_js(js, callback, escape) return c.response -import urllib2 -class EmbedHandler(urllib2.BaseHandler, urllib2.HTTPHandler, +class EmbedHandler(urllib2.BaseHandler, urllib2.HTTPHandler, urllib2.HTTPErrorProcessor, urllib2.HTTPDefaultErrorHandler): - @staticmethod - def redirect(_status): - def _redirect(url, status = None): - MethodController.redirect(url, code = _status) - return _redirect - + def http_redirect(self, req, fp, code, msg, hdrs): - codes = [301, 302, 303, 307] - map = dict((x, self.redirect(x)) for x in codes) - to = hdrs['Location'].replace('reddit.infogami.com', g.domain) - map[code](to) - raise StopIteration + to = hdrs['Location'] + h = urllib2.HTTPRedirectHandler() + r = h.redirect_request(req, fp, code, msg, hdrs, to) + return embedopen.open(r) http_error_301 = http_redirect http_error_302 = http_redirect @@ -184,17 +180,7 @@ embedopen = urllib2.OpenerDirector() embedopen.add_handler(EmbedHandler()) def proxyurl(url): - cstrs = ['%s="%s"' % (k, v.value) for k, v in c.cookies.iteritems()] - cookiestr = "; ".join(cstrs) - headers = {"Cookie":cookiestr} - - # TODO make this work on POST - data = None - r = urllib2.Request(url, data, headers) + r = urllib2.Request(url, None, {}) content = embedopen.open(r).read() return content - -__all__ = [__name for __name in locals().keys() if not __name.startswith('_') \ - or __name == '_'] - diff --git a/r2/r2/lib/c/filters.c b/r2/r2/lib/c/filters.c index bea411609..ba8a5427e 100644 --- a/r2/r2/lib/c/filters.c +++ b/r2/r2/lib/c/filters.c @@ -42,19 +42,19 @@ static PyObject * filters_uwebsafe(PyObject * self, PyObject *args) { PyObject * com; - Py_UNICODE * command; + Py_UNICODE * input_buffer; Py_UNICODE *buffer; PyObject * res; int ic=0, ib=0; int len; Py_UNICODE c; if (!(com = unicode_arg(args))) return NULL; - command = PyUnicode_AS_UNICODE(com); + input_buffer = PyUnicode_AS_UNICODE(com); len = PyUnicode_GetSize(com); buffer = (Py_UNICODE*)malloc(6*len*sizeof(Py_UNICODE)); for(ic = 0, ib = 0; ic < len; ic++, ib++) { - c = command[ic]; + c = input_buffer[ic]; if (c == '&') { buffer[ib++] = (Py_UNICODE)'&'; buffer[ib++] = (Py_UNICODE)'a'; @@ -83,7 +83,7 @@ filters_uwebsafe(PyObject * self, PyObject *args) buffer[ib] = (Py_UNICODE)';'; } else { - buffer[ib] = command[ic]; + buffer[ib] = input_buffer[ic]; } } res = PyUnicode_FromUnicode(buffer, ib); @@ -96,19 +96,19 @@ static PyObject * filters_uwebsafe_json(PyObject * self, PyObject *args) { PyObject * com; - Py_UNICODE * command; + Py_UNICODE * input_buffer; Py_UNICODE *buffer; PyObject * res; int ic=0, ib=0; int len; Py_UNICODE c; if (!(com = unicode_arg(args))) return NULL; - command = PyUnicode_AS_UNICODE(com); + input_buffer = PyUnicode_AS_UNICODE(com); len = PyUnicode_GetSize(com); buffer = (Py_UNICODE*)malloc(5*len*sizeof(Py_UNICODE)); for(ic = 0, ib = 0; ic < len; ic++, ib++) { - c = command[ic]; + c = input_buffer[ic]; if (c == '&') { buffer[ib++] = (Py_UNICODE)'&'; buffer[ib++] = (Py_UNICODE)'a'; @@ -129,7 +129,7 @@ filters_uwebsafe_json(PyObject * self, PyObject *args) buffer[ib] = (Py_UNICODE)';'; } else { - buffer[ib] = command[ic]; + buffer[ib] = input_buffer[ic]; } } res = PyUnicode_FromUnicode(buffer, ib); @@ -142,18 +142,18 @@ filters_uwebsafe_json(PyObject * self, PyObject *args) static PyObject * filters_websafe(PyObject * self, PyObject *args) { - const char * command; + const char * input_buffer; char *buffer; PyObject * res; int ic=0, ib=0; int len; char c; - if (!PyArg_ParseTuple(args, "s", &command)) + if (!PyArg_ParseTuple(args, "s", &input_buffer)) return NULL; - len = strlen(command); + len = strlen(input_buffer); buffer = (char*)malloc(5*len); for(ic = 0, ib = 0; ic <= len; ic++, ib++) { - c = command[ic]; + c = input_buffer[ic]; if (c == '&') { buffer[ib++] = '&'; buffer[ib++] = 'a'; @@ -182,7 +182,7 @@ filters_websafe(PyObject * self, PyObject *args) buffer[ib] = ';'; } else { - buffer[ib] = command[ic]; + buffer[ib] = input_buffer[ic]; } } res = Py_BuildValue("s", buffer); @@ -199,12 +199,12 @@ void print_unicode(Py_UNICODE *c, int len) { printf("\n"); } -const char *MD_START = "
"; -const char *MD_END = "
"; -const Py_UNICODE *MD_START_U; -const Py_UNICODE *MD_END_U; -int MD_START_LEN = 0; -int MD_END_LEN = 0; +const char *SC_OFF = ""; +const char *SC_ON = ""; +const Py_UNICODE *SC_OFF_U; +const Py_UNICODE *SC_ON_U; +int SC_OFF_LEN = 0; +int SC_ON_LEN = 0; @@ -218,7 +218,7 @@ filters_uspace_compress(PyObject * self, PyObject *args) { PyObject * com; PyObject * res; Py_ssize_t len; - Py_UNICODE *command; + Py_UNICODE *input_buffer; Py_UNICODE *buffer; Py_UNICODE c; int ic, ib; @@ -227,36 +227,48 @@ filters_uspace_compress(PyObject * self, PyObject *args) { if(!com) { return NULL; } - command = PyUnicode_AS_UNICODE(com); + input_buffer = PyUnicode_AS_UNICODE(com); len = PyUnicode_GetSize(com); buffer = (Py_UNICODE*)malloc(len * sizeof(Py_UNICODE)); + /* ic -> input buffer index, ib -> output buffer */ for(ic = 0, ib = 0; ic <= len; ic++) { - c = command[ic]; + c = input_buffer[ic]; + /* gobble -> we are space compressing */ if(gobble) { + /* remove spaces if encountered */ if(Py_UNICODE_ISSPACE(c)) { - while(Py_UNICODE_ISSPACE(c)) { c = command[++ic]; } + /* after this loop, c will be a non-space */ + while(Py_UNICODE_ISSPACE(c)) { c = input_buffer[++ic]; } + /* unless next char is a <, add a single space to account for + the multiple spaces that have been removed */ if(c != (Py_UNICODE)('<')) { buffer[ib++] = (Py_UNICODE)(' '); } } + /* gobble all space after '>' */ if(c == (Py_UNICODE)('>')) { buffer[ib++] = c; - c = command[++ic]; - while(Py_UNICODE_ISSPACE(c)) { c = command[++ic]; } + c = input_buffer[++ic]; + while(Py_UNICODE_ISSPACE(c)) { c = input_buffer[++ic]; } } - if (len - ic >= MD_START_LEN && - memcmp(&command[ic], MD_START_U, - sizeof(Py_UNICODE)*MD_START_LEN) == 0) { + /* does the next part of the string match the SC_OFF label */ + if (len - ic >= SC_OFF_LEN && + memcmp(&input_buffer[ic], SC_OFF_U, + sizeof(Py_UNICODE)*SC_OFF_LEN) == 0) { + /* disable gobbling, and bypass that part of the string */ gobble = 0; + ic += SC_OFF_LEN; + c = input_buffer[ic]; } } - else { - if (len - ic > MD_END_LEN && - memcmp(&command[ic], MD_END_U, - sizeof(Py_UNICODE)*MD_END_LEN) == 0) { + /* not gobbling, but find the SC_ON tag */ + else if (len - ic >= SC_ON_LEN && + memcmp(&input_buffer[ic], SC_ON_U, + sizeof(Py_UNICODE)*SC_ON_LEN) == 0) { gobble = 1; - } + ic += SC_ON_LEN; + c = input_buffer[ic]; } if(c) { buffer[ib++] = c; @@ -268,57 +280,6 @@ filters_uspace_compress(PyObject * self, PyObject *args) { return res; } - -static PyObject * -filters_space_compress(PyObject * self, PyObject *args) -{ - PyObject * res; - - const char * command; - int len, ic, ib; - char c; - char * buffer; - int gobble = 1; - if (!PyArg_ParseTuple(args, "s", &command)) - return NULL; - - len = strlen(command); - buffer = (char*)malloc(len); - - for(ic = 0, ib = 0; ic <= len; ic++) { - c = command[ic]; - if(gobble) { - if(c == '>') { - buffer[ib++] = c; - while(whitespace(command[++ic])); - c = command[ic]; - } - else if(whitespace(c)) { - while(whitespace(command[++ic])); - c = command[ic]; - if(c != '<') { - buffer[ib++] = ' '; - } - } - if (len - ic >= MD_START_LEN && - strncmp(&command[ic], MD_START, MD_START_LEN) == 0) { - gobble = 0; - } - } - else { - if (len - ic > MD_END_LEN && - strncmp(&command[ic], MD_END, MD_END_LEN) == 0) { - gobble = 1; - } - } - buffer[ib++] = c; - } - - res = Py_BuildValue("s", buffer); - free(buffer); - return res; -} - static PyMethodDef FilterMethods[] = { {"websafe", filters_websafe, METH_VARARGS, "make string web safe."}, @@ -326,10 +287,8 @@ static PyMethodDef FilterMethods[] = { "make string web safe."}, {"uwebsafe_json", filters_uwebsafe_json, METH_VARARGS, "make string web safe, no "."}, - {"space_compress", filters_space_compress, METH_VARARGS, - "returns meep"}, {"uspace_compress", filters_uspace_compress, METH_VARARGS, - "returns meep"}, + "removes spaces around angle brackets. Can be disabled with the use of SC_OFF and SC_ON comments from r2.lib.filters."}, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -347,10 +306,10 @@ Py_UNICODE *to_unicode(const char *c, int len) { PyMODINIT_FUNC initCfilters(void) { - MD_START_LEN = strlen(MD_START); - MD_START_U = to_unicode(MD_START, MD_START_LEN); - MD_END_LEN = strlen(MD_END); - MD_END_U = to_unicode(MD_END, MD_END_LEN); + SC_OFF_LEN = strlen(SC_OFF); + SC_OFF_U = to_unicode(SC_OFF, SC_OFF_LEN); + SC_ON_LEN = strlen(SC_ON); + SC_ON_U = to_unicode(SC_ON, SC_ON_LEN); (void) Py_InitModule("Cfilters", FilterMethods); } diff --git a/r2/r2/lib/filters.py b/r2/r2/lib/filters.py index e93607a86..c29a17a1f 100644 --- a/r2/r2/lib/filters.py +++ b/r2/r2/lib/filters.py @@ -25,8 +25,9 @@ import cgi import urllib import re -MD_START = '
' -MD_END = '
' +SC_OFF = "" +SC_ON = "" + def python_websafe(text): @@ -50,15 +51,23 @@ except ImportError: _between_tags1 = re.compile('> +') _between_tags2 = re.compile(' +<') _spaces = re.compile('[\s]+') - _ignore = re.compile('(' + MD_START + '.*?' + MD_END + ')', re.S | re.I) + _ignore = re.compile('(' + SC_OFF + '|' + SC_ON + ')', re.S | re.I) def spaceCompress(content): res = '' + sc = True for p in _ignore.split(content): - if not p.startswith(MD_START) and not p.endswith(MD_END): + if p == SC_ON: + sc = True + elif p == SC_OFF: + sc = False + elif sc: p = _spaces.sub(' ', p) p = _between_tags1.sub('>', p) p = _between_tags2.sub('<', p) - res += p + res += p + else: + res += p + return res class _Unsafe(unicode): pass @@ -141,7 +150,7 @@ def safemarkdown(text, nofollow = False): text = code_re.sub(code_handler, text) text = a_re.sub(inner_a_handler, text) text = fix_url.sub(r'\1', text) - return MD_START + text + MD_END + return SC_OFF + '
' + text + '
' + SC_ON def keep_space(text): diff --git a/r2/r2/lib/menus.py b/r2/r2/lib/menus.py index f79867301..9ba3f9968 100644 --- a/r2/r2/lib/menus.py +++ b/r2/r2/lib/menus.py @@ -85,20 +85,21 @@ menu = MenuHandler(hot = _('hot'), stats = _("stats"), submit = _("submit"), help = _("help"), - blog = _("blog"), + blog = _("the reddit blog"), logout = _("logout"), #reddit footer strings feedback = _("feedback"), bookmarklets = _("bookmarklets"), - socialite = _("socialite"), + socialite = _("socialite firefox extension"), buttons = _("buttons"), widget = _("widget"), - code = _("code"), + code = _("source code"), mobile = _("mobile"), store = _("store"), - ad_inq = _("advertise"), - + ad_inq = _("advertise on reddit"), + toplinks = _("top links"), + #preferences options = _('options'), friends = _("friends"), @@ -180,8 +181,9 @@ def menu_style(type): lightdrop = ('dropdown', 'lightdrop'), tabdrop = ('dropdown', 'tabdrop'), srdrop = ('dropdown', 'srdrop'), - flatlist = ('flatlist', ''), + flatlist = ('flatlist', 'flat-list'), tabmenu = ('tabmenu', ''), + flat_vert = ('flatlist', 'flat-vert'), ) return d.get(type, default) @@ -292,6 +294,10 @@ class NavButton(Styled): when it is different from self.title)""" return self.title +class OffsiteButton(NavButton): + def build(self, base_path = ''): + self.path = self.bare_path = self.dest + class SubredditButton(NavButton): def __init__(self, sr): self.sr = sr diff --git a/r2/r2/lib/pages/pages.py b/r2/r2/lib/pages/pages.py index 27e20c493..7f6f79128 100644 --- a/r2/r2/lib/pages/pages.py +++ b/r2/r2/lib/pages/pages.py @@ -32,7 +32,7 @@ from r2.lib.traffic import load_traffic, load_summary from r2.lib.captcha import get_iden from r2.lib.filters import spaceCompress, _force_unicode, _force_utf8 from r2.lib.menus import NavButton, NamedButton, NavMenu, PageNameNav, JsButton -from r2.lib.menus import SubredditButton, SubredditMenu, menu +from r2.lib.menus import SubredditButton, SubredditMenu, OffsiteButton, menu from r2.lib.strings import plurals, rand_strings, strings from r2.lib.utils import title_to_url, query_string, UrlParser, to_js from r2.lib.template_helpers import add_sr, get_domain @@ -185,21 +185,69 @@ class Reddit(Wrapped): def footer_nav(self): """navigation buttons in the footer.""" - buttons = [NamedButton("help", False, nocname=True), - NamedButton("blog", False, nocname=True), - NamedButton("stats", False, nocname=True), - NamedButton("feedback", False), - NamedButton("bookmarklets", False), - NamedButton("socialite", False), - NamedButton("buttons", True), - NamedButton("widget", True), - NamedButton("code", False, nocname=True), - NamedButton("mobile", False, nocname=True), - NamedButton("store", False, nocname=True), - NamedButton("ad_inq", False, nocname=True), - ] + return [NavMenu([NamedButton("toplinks", False), + NamedButton("mobile", False, nocname=True), + OffsiteButton("rss", dest = '/.rss'), + NamedButton("store", False, nocname=True), + NamedButton("stats", False, nocname=True), + NamedButton("feedback", False),], + title = 'site links', type = 'flat_vert', + separator = ''), - return NavMenu(buttons, base_path = "/", type = "flatlist") + NavMenu([NamedButton("help", False, nocname=True), + OffsiteButton("FAQ", dest = '/help/faq', + nocname=True), + OffsiteButton("reddiquette", nocname=True, + dest = '/help/reddiquette')], + title = 'help', type = 'flat_vert', + separator = ''), + + NavMenu([NamedButton("bookmarklets", False), + NamedButton("buttons", True), + NamedButton("code", False, nocname=True), + NamedButton("socialite", False), + NamedButton("widget", True)], + title = 'reddit tools', type = 'flat_vert', + separator = ''), + + NavMenu([NamedButton("blog", False, nocname=True), + OffsiteButton("our pet fish", + dest="http://justin.tv/reddit"), + NamedButton("ad_inq", False, nocname=True), + OffsiteButton('reddit.tv', "http://www.reddit.tv"), + OffsiteButton('redditall', "http://www.redditall.com"),], + title = 'about us', type = 'flat_vert', + separator = ''), + NavMenu([OffsiteButton('BaconBuzz', + "http://www.baconbuzz.com"), + OffsiteButton('Destructoid reddit', + "http://reddit.destructoid.com"), + OffsiteButton('TheCuteList', + "http://www.thecutelist.com"), + OffsiteButton('The Independent reddit', + "http://reddit.independent.co.uk"), + OffsiteButton('redditGadgetGuide', + "http://www.redditgadgetguide.com"), + OffsiteButton('WeHeartGossip', + "http://www.weheartgossip.com")], + title = 'brothers', type = 'flat_vert', + separator = ''), + NavMenu([OffsiteButton('Wired.com', + "http://www.wired.com"), + OffsiteButton('Ars Technica', + "http://www.arstechnica.com"), + OffsiteButton('Style.com', + "http://www.style.com"), + OffsiteButton('Portfolio.com', + "http://www.portfolio.com", + css_class="line-through"), + OffsiteButton('Epicurious.com', + "http://www.epicurious.com"), + OffsiteButton('Concierge.com', + "http://www.concierge.com")], + title = 'sisters', type = 'flat_vert', + separator = '') + ] def build_toolbars(self): """Sets the layout of the navigation topbar on a Reddit. The result @@ -359,6 +407,9 @@ class BoringPage(Reddit): def build_toolbars(self): return [PageNameNav('nomenu', title = self.pagename)] +class HelpPage(BoringPage): + def build_toolbars(self): + return [PageNameNav('help', title = self.pagename)] class FormPage(BoringPage): """intended for rendering forms with no rightbox needed or wanted""" diff --git a/r2/r2/models/account.py b/r2/r2/models/account.py index aedfc74ff..b8d9117f6 100644 --- a/r2/r2/models/account.py +++ b/r2/r2/models/account.py @@ -61,6 +61,7 @@ class Account(Thing): has_subscribed = False, pref_media = 'subreddit', share = {}, + wiki_override = None, ) def karma(self, kind, sr = None): @@ -105,6 +106,13 @@ class Account(Thing): karma = self.link_karma return max(karma, 1) if karma > -1000 else karma + def can_wiki(self): + if self.wiki_override is not None: + return self.wiki_override + else: + return (self.link_karma >= g.WIKI_KARMA and + self.comment_karma >= g.WIKI_KARMA) + def all_karmas(self): """returns a list of tuples in the form (name, link_karma, comment_karma)""" diff --git a/r2/r2/public/static/css/reddit.css b/r2/r2/public/static/css/reddit.css index 53e6d6b58..0ecf7bbd9 100644 --- a/r2/r2/public/static/css/reddit.css +++ b/r2/r2/public/static/css/reddit.css @@ -19,7 +19,7 @@ body { z-index: 1; } -html,body { height: 100%; } +/*html,body { height: 100%; }*/ /* IE dumbness patch. hidden input in a hidden block that is * subsequently shown leads to the input to "show" and generate undesired @@ -45,15 +45,10 @@ a:focus { -moz-outline-style: none; } */ .rounded { - -moz-border-radius-topleft: 7px; - -moz-border-radius-topright: 7px; - -moz-border-radius-bottomleft: 7px; - -moz-border-radius-bottomright: 7px; - -webkit-border-top-left-radius: 7px; - -webkit-border-top-right-radius: 7px; - -webkit-border-bottom-left-radius: 7px; - -webkit-border-bottom-right-radius: 7px; + -moz-border-radius: 7px; + -webkit-border-radius: 7px; } + .rounded .morelink { -webkit-border-top-right-radius: 6px; -moz-border-radius-topright: 6px; @@ -98,6 +93,17 @@ input[type=checkbox], input[type=radio] { margin-top: .4em; } .flat-list form {display: inline; } .flat-list .selected a { color: orangered; } +ul.flat-vert {text-align: left;} +.flat-vert .separator { margin: 0 } + +.flat-vert.title { + font-family:helvetica,arial,verdana,sans-serif; + color: #777; + font-size: 18px; + font-weight: normal; + margin-bottom: 5px; + } + .separator { color: gray; margin: 0px .7ex 0px .7ex} .pref-lang { font-weight: bold; } @@ -562,6 +568,9 @@ before enabling */ .help p, .help form { margin: 5px; } .help form { display: inline; } +.wikipage { + margin: 15px; +} /* organic listing */ .organic-listing { @@ -640,7 +649,7 @@ before enabling */ .infobar { background-color: #f6e69f; padding: 5px 10px; - margin: 5px 310px 5px 0px; + margin: 5px 305px 5px 0px; border: 1px solid orange; font-size: small; } @@ -754,7 +763,7 @@ a.star { text-decoration: none; color: #ff8b60 } } /* compressed links */ -.linkcompressed { margin: 4px 0; overflow: hidden; } +.linkcompressed { margin: 4px 0; overflow: hidden; margin-top: 5px; } .linkcompressed .title {margin-bottom: 1px; font-size:medium; font-weight: normal;} .linkcompressed .child h3 { margin: 15px; @@ -765,7 +774,7 @@ a.star { text-decoration: none; color: #ff8b60 } .linkcompressed .score.likes { color: #FF8B60; } .linkcompressed .score.dislikes { color: #9494FF; } .linkcompressed .rank { - margin-top: 4px; + margin-top: 5px; float:left; color: #c6c6c6; font-family: arial; @@ -780,7 +789,7 @@ a.star { text-decoration: none; color: #ff8b60 } .linkcompressed .entry .buttons li.first {padding-left: .5em;} .linkcompressed .entry .buttons li a { padding: 0 2px; - background-color: #f3f3f3; + background-color: #f4f4f4; font-weight: bold } @@ -967,23 +976,29 @@ textarea.gray { color: gray; } /* footer */ -.footer { - width: 100%; - text-align: center; - clear: both; - padding-top: 1em; - color: gray; - font-size: larger; +.footer-parent { + text-align: center; } -.footer p { margin: 10px } +.footer { + clear: both; + color: gray; + font-size: larger; + padding: 5px; + margin: 15px; + border:1px solid #F0F0F0; + display: inline-block; +} -.wired a {text-decoration: underline; - color: #369; - font-size: smaller; - padding-left: 5px; - padding-right: 5px; } -.wired img {vertical-align: middle;} +.footer .col { + float: left; + margin: 10px; + padding-left: 10px; + border-left: 1px solid #E0E0E0; + height: 150px; + } + +.footer .col:first-child {border: none;} .server-status { width: 300px; } .server-status table { @@ -1095,6 +1110,7 @@ textarea.gray { color: gray; } .status { color: red; } .error { color: red; font-size: small; margin: 5px; } +.line-through { text-decoration: line-through } #noresults { margin-right: 310px; } @@ -1274,7 +1290,7 @@ textarea.gray { color: gray; } font-weight: bold; } -.bottommenu { color: gray; font-size: smaller; } +.bottommenu { color: gray; font-size: smaller; clear: both} .bottommenu a { color: gray; text-decoration: underline; } diff --git a/r2/r2/templates/helppage.html b/r2/r2/templates/helppage.html new file mode 100644 index 000000000..b16ef765a --- /dev/null +++ b/r2/r2/templates/helppage.html @@ -0,0 +1,30 @@ +## The contents of this file are subject to the Common Public Attribution +## License Version 1.0. (the "License"); you may not use this file except in +## compliance with the License. You may obtain a copy of the License at +## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public +## License Version 1.1, but Sections 14 and 15 have been added to cover use of +## software over a computer network and provide for limited attribution for the +## Original Developer. In addition, Exhibit A has been modified to be consistent +## with Exhibit B. +## +## Software distributed under the License is distributed on an "AS IS" basis, +## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +## the specific language governing rights and limitations under the License. +## +## The Original Code is Reddit. +## +## The Original Developer is the Initial Developer. The Initial Developer of +## the Original Code is CondeNet, Inc. +## +## All portions of the code written by CondeNet are Copyright (c) 2006-2009 +## CondeNet, Inc. All Rights Reserved. +################################################################################ + +<%inherit file="reddit.html"/> + +<%def name="stylesheet()"> + ${parent.stylesheet()} + + diff --git a/r2/r2/templates/navmenu.html b/r2/r2/templates/navmenu.html index 1dd1bacff..3e060d4ad 100644 --- a/r2/r2/templates/navmenu.html +++ b/r2/r2/templates/navmenu.html @@ -57,12 +57,13 @@ <%def name="flatlist()"> <% css_class = str(thing.css_class) if thing.css_class else "" %> %if thing: - %if thing.title: - ${thing.title}: - %endif - -