# HG changeset patch # User Tero Marttila # Date 1234052963 -7200 # Node ID 5a72c00c4ae4a1b8733cede79340b632e7eb3ca6 # Parent 9585441a4bfbf4f64e5a7bc71531fa95b640cff5 more fiddling around with the irclogs layout/css, add query args to URL diff -r 9585441a4bfb -r 5a72c00c4ae4 lib/helpers.py --- a/lib/helpers.py Sun Feb 08 00:29:36 2009 +0200 +++ b/lib/helpers.py Sun Feb 08 02:29:23 2009 +0200 @@ -20,6 +20,15 @@ return time.strftime("%Y") +def validation_notice (site_host) : + """ + Returns a short "Validated XHTML & CSS" link text for the given site hostname + """ + + return 'Validated XHTML 1.0 Strict & CSS 2.1' % dict( + host = site_host + ) + def breadcrumb (trail, links=True) : """ Returns a nicely formatted breadcrumb tail, optinally with links diff -r 9585441a4bfb -r 5a72c00c4ae4 lib/http.py --- a/lib/http.py Sun Feb 08 00:29:36 2009 +0200 +++ b/lib/http.py Sun Feb 08 02:29:23 2009 +0200 @@ -29,8 +29,17 @@ # parse query args self.arg_dict = cgi.parse_qs(self.arg_str, True) - - def get_script_dir (self) : + + @property + def site_host (self) : + """ + Returns the site's hostname (DNS name) + """ + + return self.env['HTTP_HOST'] + + @property + def site_root (self) : """ Returns the URL path to the requested script's directory with no trailing slash, i.e. @@ -41,7 +50,8 @@ return os.path.dirname(self.env['SCRIPT_NAME']).rstrip('/') - def get_page_prefix (self) : + @property + def page_prefix (self) : """ Returns the URL path root for page URLs, based on REQUEST_URI with PATH_INFO removed @@ -68,7 +78,7 @@ # trim return request_path[:-len(page_name)].rstrip('/') - + def get_page_name (self) : """ Returns the requested page path with no leading slash, i.e. @@ -88,6 +98,13 @@ else : return '' + + def get_args (self) : + """ + Iterate over all available (key, value) pairs from the query string + """ + + return cgi.parse_qsl(self.arg_str) class Response (object) : """ diff -r 9585441a4bfb -r 5a72c00c4ae4 lib/template.py --- a/lib/template.py Sun Feb 08 00:29:36 2009 +0200 +++ b/lib/template.py Sun Feb 08 02:29:23 2009 +0200 @@ -28,45 +28,51 @@ pass -def render (tpl, **params) : - """ - Render the given template, returning the output as a unicode string, or raising a TemplateError - """ - - try : - return tpl.render_unicode( - # global helper stuff - h = helpers, - - # render-specific params - **params - ) - - # a template may render other templates - except TemplateError : - raise - - except : - details = exceptions.text_error_template().render() - - raise TemplateError("Template render failed", status='500 Internal Server Error', details=details) - class TemplateLoader (mako.lookup.TemplateLookup) : """ Our own specialization of mako's TemplateLookup """ - def __init__ (self, path, fileext=TEMPLATE_EXT) : + def __init__ (self, path, fileext=TEMPLATE_EXT, **env) : """ - Initialize to load templates located at path, with the given file extension + Initialize to load templates located at path, with the given file extension. + + The given **env list is supplied to every template render """ # store self.path = path self.fileext = fileext + self.env = env + + # build the TemplateLookup + super(TemplateLoader, self).__init__(directories=[path], module_directory=CACHE_DIR) + + @staticmethod + def _render (tpl, env, params) : + """ + Render the given template with given env/params, returning the output as a unicode string, or raising a TemplateError + """ + + # build the context from our superglobals, env, and params + ctx = dict( + h = helpers, + ) + ctx.update(env) + ctx.update(params) + + try : + return tpl.render_unicode(**ctx) - # XXX: separate cache? - super(TemplateLoader, self).__init__(directories=[path], module_directory=CACHE_DIR) + # a template may render other templates + except TemplateError : + raise + + except : + details = exceptions.text_error_template().render() + + raise TemplateError("Template render failed", status='500 Internal Server Error', details=details) + def lookup (self, name) : """ @@ -84,7 +90,7 @@ Render a template, using lookup() on the given name """ - return render(self.lookup(name), **params) + return self._render(self.lookup(name), self.env, params) def render_to_response (self, name, **params) : """ @@ -108,9 +114,15 @@ @classmethod def render_file (cls, path, **params) : """ - Render a template, using load() on the given path + Render a template, using load() on the given path. No global environment vars are defined for the render. """ - return render(cls.load(path), **params) + return cls._render(cls.load(path), dict(), params) - + @classmethod + def render_template (cls, template, **params) : + """ + Render the given template object. No global environment vars are defined for the render. + """ + + return cls._render(template, dict(), params) diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/channels.py --- a/sites/irclogs.qmsk.net/channels.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/channels.py Sun Feb 08 02:29:23 2009 +0200 @@ -22,6 +22,9 @@ 'tycoon': LogChannel('tycoon', "OFTC", "#tycoon", LogDirectory(relpath('logs/tycoon'), pytz.timezone('Europe/Helsinki')) ), + 'openttd': LogChannel('openttd', "OFTC", "#openttd", + LogDirectory(relpath('logs/openttd'), pytz.timezone('Europe/Helsinki')) + ), } def __init__ (self, channels) : diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/handlers.py --- a/sites/irclogs.qmsk.net/handlers.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/handlers.py Sun Feb 08 02:29:23 2009 +0200 @@ -4,15 +4,29 @@ from lib import http, template +import urls, channels + # load templates from here -templates = template.TemplateLoader("sites/irclogs.qmsk.net/templates") +templates = template.TemplateLoader("sites/irclogs.qmsk.net/templates", + urls = urls, + channel_list = channels.channel_list, +) def index (request) : """ The topmost index page, display a list of available channels, perhaps some general stats """ - return templates.render_to_response("index") + return templates.render_to_response("index", + req = request, + ) + +def channel_select (request, channel) : + """ + Redirect to the appropriate channel_view + """ + + return http.Redirect(urls.channel_view.build(request, channel=channel.id)) def channel_view (request, channel) : """ @@ -20,6 +34,7 @@ """ return templates.render_to_response("channel", + req = request, channel = channel, ) diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/log_channel.py --- a/sites/irclogs.qmsk.net/log_channel.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/log_channel.py Sun Feb 08 02:29:23 2009 +0200 @@ -16,4 +16,12 @@ self.network = network self.name = name self.source = source + + @property + def title (self) : + """ + Title is 'Network - #channel' + """ + return "%s - %s" % (self.network, self.name) + diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/logs/openttd --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sites/irclogs.qmsk.net/logs/openttd Sun Feb 08 02:29:23 2009 +0200 @@ -0,0 +1,1 @@ +/home/terom/backups/zapotek-irclogs/#openttd \ No newline at end of file diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/templates/channel.tmpl --- a/sites/irclogs.qmsk.net/templates/channel.tmpl Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/templates/channel.tmpl Sun Feb 08 02:29:23 2009 +0200 @@ -1,6 +1,45 @@ -

Channel ${channel.name}

+<%inherit file="layout.tmpl" /> -

Last 10 lines:

+<%def name="menu()"> + + + +

${channel.title} » Last 10 lines

+
 % for line in channel.source.get_latest(10) :
 ${line}
diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/templates/index.tmpl
--- a/sites/irclogs.qmsk.net/templates/index.tmpl	Sun Feb 08 00:29:36 2009 +0200
+++ b/sites/irclogs.qmsk.net/templates/index.tmpl	Sun Feb 08 02:29:23 2009 +0200
@@ -1,2 +1,9 @@
-Index page template
+<%inherit file="layout.tmpl" />
 
+

Available Channels

+ + diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/templates/layout.tmpl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sites/irclogs.qmsk.net/templates/layout.tmpl Sun Feb 08 02:29:23 2009 +0200 @@ -0,0 +1,31 @@ + + +<%def name="menu()"> + + + + + + irclogs.qmsk.net ${('::' + channel.title) if channel else ''} + + + + + +
+ ${next.body()} +
+ + + + + diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/urls.py --- a/sites/irclogs.qmsk.net/urls.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/urls.py Sun Feb 08 02:29:23 2009 +0200 @@ -26,12 +26,13 @@ # urls index = url('/', handlers.index ) +channel_select = url('/channel_select/?channel:cid', handlers.channel_select ) channel_view = url('/channels/{channel:cid}', handlers.channel_view ) channel_last = url('/channels/{channel:cid}/last/{count:int=100}/{format=html}', handlers.channel_last ) channel_search = url('/channels/{channel:cid}/search', handlers.channel_search ) # mapper mapper = URLTree( - [index, channel_view, channel_last, channel_search] + [index, channel_select, channel_view, channel_last, channel_search] ) diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/irclogs.qmsk.net/urltree.py --- a/sites/irclogs.qmsk.net/urltree.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/irclogs.qmsk.net/urltree.py Sun Feb 08 02:29:23 2009 +0200 @@ -93,6 +93,13 @@ """ abstract + + def build (self, value_dict) : + """ + Return a string representing this label, using the values in the given value_dict if needed + """ + + abstract class EmptyLabel (Label) : """ @@ -118,6 +125,9 @@ # only empty segments if value == '' : return True + + def build (self, values) : + return str(self) def __str__ (self) : return '' @@ -156,6 +166,9 @@ if value == self.name : return True + def build (self, values) : + return str(self) + def __str__ (self) : return self.name @@ -180,6 +193,21 @@ """ return isinstance(other, ValueLabel) and self.key == other.key + + def build (self, values) : + """ + Return either the assigned value from values, our default value, or raise an error + """ + + value = values.get(self.key) + + if not value and self.default : + value = self.default + + elif not value : + raise URLError("No value given for label %r" % (self.key, )) + + return value class SimpleValueLabel (ValueLabel) : """ @@ -273,9 +301,43 @@ self.handler = handler self.defaults = defaults - # build our labels + # query string + self.query_args = dict() + + # parse any query string + # XXX: conflicts with regexp syntax + if '/?' in url_mask : + url_mask, query_mask = url_mask.split('/?') + + else : + query_mask = None + + # build our label path self.label_path = [Label.parse(mask, defaults, config.type_dict) for mask in url_mask.split('/')] - + + # build our query args list + if query_mask : + # split into items + for query_item in query_mask.split('&') : + # parse default + if '=' in query_item : + query_item, default = query_item.split('=') + + else : + default = None + + # parse type + if ':' in query_item : + query_item, type = query_item.split(':') + else : + type = None + + # parse key + key = query_item + + # add to query_args as (type, default) tuple + self.query_args[key] = (self.config.type_dict[type], default) + def get_label_path (self) : """ Returns a list containing the labels in this url @@ -295,9 +357,46 @@ # then add all the values for label_value in label_values : kwargs[label_value.label.key] = label_value.value + + # then parse all query args + # XXX: catch missing arguments + for key, value in request.get_args() : + # lookup spec + type, default = self.query_args[key] + + # normalize empty value to None + if not value : + value = None + + else : + # process value + value = type(value) + + # set default? + if not value : + if default : + value = default + + if default == '' : + # do not pass key at all + continue + + # otherwise, fail + raise URLError("No value given for required argument: %r" % (key, )) + # set key + kwargs[key] = value + # execute the handler return self.handler(request, **kwargs) + + def build (self, request, **values) : + """ + Build an absolute URL pointing to this target, with the given values + """ + + # build URL from request page prefix and our labels + return request.page_prefix + '/'.join(label.build(values) for label in self.label_path) def __str__ (self) : return '/'.join(str(label) for label in self.label_path) diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/www.qmsk.net/page.py --- a/sites/www.qmsk.net/page.py Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/www.qmsk.net/page.py Sun Feb 08 02:29:23 2009 +0200 @@ -99,10 +99,8 @@ """ # render the template - response_data = template.render(self.fs.template, - request = request, - site_root_url = request.get_script_dir(), - site_page_url = request.get_page_prefix(), + response_data = template.TemplateLoader.render_template(self.fs.template, + req = request, page = self, menu = menu.Menu(self.fs, self), ) diff -r 9585441a4bfb -r 5a72c00c4ae4 sites/www.qmsk.net/templates/layout.tmpl --- a/sites/www.qmsk.net/templates/layout.tmpl Sun Feb 08 00:29:36 2009 +0200 +++ b/sites/www.qmsk.net/templates/layout.tmpl Sun Feb 08 02:29:23 2009 +0200 @@ -4,7 +4,7 @@