<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>utils/django_libs/find_static.py</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -33,7 +33,7 @@ BLOG = {
     # Use the default YUI-based theme.
     # If another string is used besides 'default', calls to static files and
     #  use of template files in /views will go to directory by that name.
-    &quot;theme&quot;: &quot;default&quot;,
+    &quot;theme&quot;: [&quot;default&quot;],
     
     # Display gravatars alongside user comments?
     &quot;use_gravatars&quot;: True,</diff>
      <filename>config.py</filename>
    </modified>
    <modified>
      <diff>@@ -301,8 +301,9 @@ def process_comment_submission(handler, article):
                        body=body)
 
     # Render just this comment and send it to client
+    view_path = view.find_file(view.templates, &quot;bloog/blog/comment.html&quot;)
     response = template.render(
-        &quot;views/%s/bloog/blog/comment.html&quot; % config.BLOG['theme'], 
+        os.path.join(&quot;views&quot;, view_path),
         { 'comment': comment }, debug=config.DEBUG)
     handler.response.out.write(response)
     view.invalidate_cache()</diff>
      <filename>handlers/bloog/blog.py</filename>
    </modified>
    <modified>
      <diff>@@ -39,6 +39,7 @@ sys.path.insert(0, config.APP_ROOT_DIR)
 # Import custom django libraries
 webapp.template.register_template_library('utils.django_libs.gravatar')
 webapp.template.register_template_library('utils.django_libs.description')
+webapp.template.register_template_library('utils.django_libs.find_static')
 
 # Log a message each time this module get loaded.
 logging.info('Loading %s, app version = %s',</diff>
      <filename>main.py</filename>
    </modified>
    <modified>
      <diff>@@ -43,6 +43,7 @@ http://www.djangoproject.com/documentation/templates/
 &quot;&quot;&quot;
 
 
+import logging
 import os
 
 try:
@@ -98,11 +99,14 @@ def load(path, debug=False, template_dirs=()):
 
   if not template:
     directory, file_name = os.path.split(abspath)
+    if directory:
+      template_dirs = [directory] + template_dirs
+    logging.warn(template_dirs, file_name)
     new_settings = {
-        'TEMPLATE_DIRS': (directory,) + template_dirs,
+        'TEMPLATE_DIRS': template_dirs,
         'TEMPLATE_DEBUG': debug,
         'DEBUG': debug,
-        }
+    }
     old_settings = _swap_settings(new_settings)
     try:
       template = django.template.loader.get_template(file_name)</diff>
      <filename>utils/template.py</filename>
    </modified>
    <modified>
      <diff>@@ -37,6 +37,32 @@ import config
 
 NUM_FULL_RENDERS = {}       # Cached data for some timings.
 
+def do_build_tree(base, path, tree):
+    for entry in os.listdir(os.path.join(base, path)):
+        entry_path = os.path.join(path, entry)
+        if os.path.isdir(os.path.join(base, entry_path)):
+            do_build_tree(base, entry_path, tree.setdefault(entry, {}))
+        elif entry not in tree:
+            tree[entry] = entry_path
+
+def build_tree(base):
+    tree = {}
+    basedir = os.path.join(config.APP_ROOT_DIR, base)
+    for theme in config.BLOG['theme']:
+        do_build_tree(basedir, theme, tree)
+    return tree
+templates = build_tree('views')
+staticfiles = build_tree('static')
+
+def find_file(tree, path):
+    cur = tree
+    for element in path.split('/'):
+        cur = cur.get(element, {})
+    if cur:
+        return cur
+    else:
+        return None
+
 def invalidate_cache():
     memcache.flush_all()
 
@@ -93,36 +119,37 @@ def get_view_file(handler, params={}):
 
     # Get template directory hierarchy -- Needed if we inherit from templates
     # in directories above us (due to sharing with other templates).
-    
-    root_folder = os.path.join(
-        config.APP_ROOT_DIR, 
-        'views', config.BLOG['theme'])
-    template_dirs = ()
-    if module_name:
-        template_dirs += (os.path.join(root_folder, app_name, module_name),)
-    if app_name:
-        template_dirs += (os.path.join(root_folder, app_name),)
-    template_dirs += (root_folder,)
+    themes = config.BLOG['theme']
+    if isinstance(themes, basestring):
+      themes = [themes]
+    template_dirs = []
+    views_dir = os.path.join(config.APP_ROOT_DIR, 'views')
+    for theme in themes:
+      root_folder = os.path.join(views_dir, theme)
+      if module_name:
+          template_dirs += (os.path.join(root_folder, app_name, module_name),)
+      if app_name:
+          template_dirs += (os.path.join(root_folder, app_name),)
+      template_dirs += (root_folder,)
         
     # Now check possible extensions for the given template file.
     if module_name and handler_name:
-        filename_prefix = os.path.join(root_folder, app_name, module_name, handler_name)
+        entries = templates.get(app_name, {}).get(module_name, {})
         possible_roles = []
         if users.is_current_user_admin():
             possible_roles.append('.admin.')
         if users.get_current_user():
             possible_roles.append('.user.')
         possible_roles.append('.')
-        # Check possible template file names in order of decreasing priority
         for role in possible_roles:
-            filename = filename_prefix + role + verb + '.' + desired_ext
-            if os.path.exists(filename):
+            filename = ''.join([handler_name, role, verb, '.', desired_ext])
+            if filename in entries:
                 return {'file': filename, 'dirs': template_dirs}
         for role in possible_roles:
-            filename = filename_prefix + role + desired_ext
-            if os.path.exists(filename):
+            filename = ''.join([handler_name, role, desired_ext])
+            if filename in entries:
                 return {'file': filename, 'dirs': template_dirs}
-    return {'file': root_folder + '/notfound.html', 'dirs': template_dirs}
+    return {'file': 'notfound.html', 'dirs': template_dirs}
 
 class ViewPage(object):
     def __init__(self, cache_time=None):</diff>
      <filename>view.py</filename>
    </modified>
    <modified>
      <diff>@@ -4,7 +4,7 @@
         &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;{{ blog.html_type }}; charset={{ blog.charset }}&quot; /&gt;
         &lt;title&gt; {{ title }}&lt;/title&gt;
         &lt;meta name=&quot;generator&quot; content=&quot;Bloogle {{ bloogle_version }}&quot; /&gt;
-        &lt;link rel=&quot;stylesheet&quot; href=&quot;/static/default/style.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; /&gt;
+        &lt;link rel=&quot;stylesheet&quot; href=&quot;{{&quot;style.css&quot;|find_static_file}}&quot; type=&quot;text/css&quot; media=&quot;screen&quot; /&gt;
         &lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot; title=&quot;{{ blog.title }} RSS Feed&quot; 
               href=&quot;{{ blog.root_url }}{{ blog.master_atom_url }}&quot; /&gt;
         &lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;http://yui.yahooapis.com/2.5.2/build/assets/skins/sam/skin.css&quot; /&gt;
@@ -18,7 +18,7 @@
             &lt;div id=&quot;masthead&quot; class=&quot;fix&quot;&gt;
                 &lt;h1&gt;&lt;a href=&quot;/&quot;&gt;{{ blog.title }}&lt;/a&gt;&lt;/h1&gt;
                 &lt;div id=&quot;authorBlurb&quot;&gt;
-                    &lt;img src=&quot;/static/default/images/avatar.png&quot; alt=&quot;Avatar&quot; /&gt;
+                    &lt;img src=&quot;{{&quot;images/avatar.png&quot;|find_static_file}}&quot; alt=&quot;Avatar&quot; /&gt;
                     &lt;p id=&quot;authorIntro&quot;&gt;
                         {{ blog.description }}
                     &lt;/p&gt;
@@ -89,7 +89,7 @@
                     &lt;div id=&quot;searchWrap&quot;&gt;
                         &lt;form method=&quot;get&quot; id=&quot;searchForm&quot; action=&quot;/search&quot;&gt;
                             &lt;input type=&quot;text&quot; value=&quot;{{ searchterm }}&quot; name=&quot;s&quot; id=&quot;s&quot; /&gt;
-                            &lt;input id=&quot;searchsubmit&quot; type=&quot;image&quot; src=&quot;/static/default/images/btn_search.gif&quot; alt=&quot;Search&quot; /&gt;
+                            &lt;input id=&quot;searchsubmit&quot; type=&quot;image&quot; src=&quot;{{&quot;images/btn_search.gif&quot;|find_static_file}}&quot; alt=&quot;Search&quot; /&gt;
                         &lt;/form&gt;
                     &lt;/div&gt;
                     {% endblock %}
@@ -254,7 +254,7 @@
         &lt;/div&gt;
 
         &lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&quot;&gt;&lt;/script&gt;
-        &lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/bloog_base.js&quot;&gt;&lt;/script&gt;
+        &lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/bloog_base.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
 
         {% if user_is_admin %}
         {% include &quot;form_editor.html&quot; %}</diff>
      <filename>views/default/bloog/base.html</filename>
    </modified>
    <modified>
      <diff>@@ -37,8 +37,8 @@ p span.displaynone { display:none; }
 {% if not user_is_admin %}
 &lt;!-- Load scripts if not already loaded due to admin interface --&gt;
 &lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/combo?2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&amp;2.5.2/build/animation/animation-min.js&amp;2.5.2/build/connection/connection-min.js&amp;2.5.2/build/container/container-min.js&amp;2.5.2/build/menu/menu-min.js&amp;2.5.2/build/element/element-beta-min.js&amp;2.5.2/build/button/button-min.js&amp;2.5.2/build/editor/editor-beta-min.js&amp;2.5.2/build/selector/selector-beta-min.js&quot;&gt;&lt;/script&gt;
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/ojay/js-class.js&quot;&gt;&lt;/script&gt;
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/ojay/core.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/ojay/js-class.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/ojay/core.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
 {% endif %}
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/bloog_comments.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/bloog_comments.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
 </diff>
      <filename>views/default/bloog/blog/form_comment.html</filename>
    </modified>
    <modified>
      <diff>@@ -21,7 +21,7 @@
             &lt;p&gt;&lt;input type=&quot;text&quot; id=&quot;subject&quot; name=&quot;subject&quot; size=&quot;22&quot; tabindex=&quot;3&quot; /&gt;
                 &lt;label for=&quot;subject&quot;&gt;&lt;small&gt;Subject&lt;/small&gt;&lt;/label&gt;&lt;/p&gt;
             &lt;p&gt;&lt;textarea id=&quot;message&quot; name=&quot;message&quot; cols=&quot;60&quot; rows=&quot;10&quot; tabindex=&quot;4&quot;&gt;Please enter a message.&lt;/textarea&gt;&lt;/p&gt;
-            &lt;p&gt;&lt;input name=&quot;submit&quot; type=&quot;image&quot; src=&quot;/static/default/images/btn_submit.gif&quot; alt=&quot;Submit&quot; id=&quot;submit&quot; tabindex=&quot;5&quot; value=&quot;Submit&quot; /&gt;&lt;/p&gt;
+            &lt;p&gt;&lt;input name=&quot;submit&quot; type=&quot;image&quot; src=&quot;{{&quot;images/btn_submit.gif&quot;|find_static_file}}&quot; alt=&quot;Submit&quot; id=&quot;submit&quot; tabindex=&quot;5&quot; value=&quot;Submit&quot; /&gt;&lt;/p&gt;
             &lt;input type=&quot;hidden&quot; name=&quot;token&quot; value=&quot;{{ token }}&quot; /&gt;
             &lt;input type=&quot;hidden&quot; name=&quot;curtime&quot; value=&quot;{{ curtime|floatformat }}&quot; /&gt;
         &lt;/form&gt;</diff>
      <filename>views/default/bloog/contact/contact.get.html</filename>
    </modified>
    <modified>
      <diff>@@ -1,7 +1,7 @@
 {% if user_is_admin %}
 
 {% block head %}
-&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/static/default/editor.css&quot; /&gt;
+&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{&quot;editor.css&quot;|find_static_file}}&quot; /&gt;
 {% endblock %}
 
 &lt;div id=&quot;postDialog&quot; class=&quot;popupDialog&quot;&gt;
@@ -27,9 +27,9 @@
 &lt;/div&gt;
 
 &lt;script type=&quot;text/javascript&quot; src=&quot;http://yui.yahooapis.com/combo?2.5.2/build/yahoo-dom-event/yahoo-dom-event.js&amp;2.5.2/build/animation/animation-min.js&amp;2.5.2/build/connection/connection-min.js&amp;2.5.2/build/container/container-min.js&amp;2.5.2/build/menu/menu-min.js&amp;2.5.2/build/element/element-beta-min.js&amp;2.5.2/build/button/button-min.js&amp;2.5.2/build/editor/editor-beta-min.js&amp;2.5.2/build/selector/selector-beta-min.js&quot;&gt;&lt;/script&gt;
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/ojay/js-class.js&quot;&gt;&lt;/script&gt;
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/ojay/core.js&quot;&gt;&lt;/script&gt;
-&lt;script type=&quot;text/javascript&quot; src=&quot;/static/default/js/bloog_admin.js&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/ojay/js-class.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/ojay/core.js&quot;|find_static_file}}&quot;&quot;&gt;&lt;/script&gt;
+&lt;script type=&quot;text/javascript&quot; src=&quot;{{&quot;js/bloog_admin.js&quot;|find_static_file}}&quot;&gt;&lt;/script&gt;
 
 {% endif %}
 </diff>
      <filename>views/default/bloog/form_editor.html</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>c1b6f3e2691aa9ce1ba274af98ddacf6c0f9fb0a</id>
    </parent>
  </parents>
  <author>
    <name>Nick Johnson</name>
    <email>arachnid@notdot.net</email>
  </author>
  <url>http://github.com/DocSavage/bloog/commit/0388917b25993a6e52ac223f133d71180dee63da</url>
  <id>0388917b25993a6e52ac223f133d71180dee63da</id>
  <committed-date>2008-11-23T13:39:29-08:00</committed-date>
  <authored-date>2008-11-23T13:39:29-08:00</authored-date>
  <message>Added support for theme inheritance.

Themes are now specified as an array rather than a single element. Files are
looked for in templates (and static directories) in the order they are listed.
This way, it is possible to define a new theme that makes only minor modifications of the default theme without copying all the files.</message>
  <tree>a1b1eaa5c1adeb507453d06e973ce478cf55b1a7</tree>
  <committer>
    <name>Nick Johnson</name>
    <email>arachnid@notdot.net</email>
  </committer>
</commit>
