]> git.openstreetmap.org Git - osqa.git/commitdiff
Starts preparing the app for module based html injection.
authorhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Sat, 19 Jun 2010 02:04:14 +0000 (02:04 +0000)
committerhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Sat, 19 Jun 2010 02:04:14 +0000 (02:04 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@438 0cfe37f9-358a-4d5e-be75-b63607b5c754

forum/modules/decorators.py
forum/modules/ui.py [new file with mode: 0644]
forum/modules/ui_objects.py [new file with mode: 0644]
forum/skins/__init__.py
forum/skins/default/templates/header.html
forum/startup.py
forum/templatetags/ui_registry.py [new file with mode: 0644]
forum/utils/html.py

index 90109a99d0fc916867f6997fce4f46d39844c45f..8fbee7e30d4b19c9475a77da0b682051e6753e86 100644 (file)
@@ -1,31 +1,68 @@
 import inspect\r
 \r
 class DecoratableObject(object):\r
+    MODE_OVERRIDE = 0\r
+    MODE_PARAMS = 1\r
+    MODE_RESULT = 2\r
+\r
     def __init__(self, fn):\r
+        a = inspect.getargspec(fn)\r
         self._callable = fn\r
+        self._params_decoration = None\r
+        self._result_decoration = None\r
 \r
-    def _decorate(self, fn, needs_origin):\r
+    def _decorate(self, fn, needs_origin, method=False):\r
         origin = self._callable\r
 \r
         if needs_origin:\r
-            self._callable = lambda *args, **kwargs: fn(origin, *args, **kwargs)\r
+            if method:\r
+                self._callable = lambda inst, *args, **kwargs: fn(inst, origin, *args, **kwargs)\r
+            else:\r
+                self._callable = lambda *args, **kwargs: fn(origin, *args, **kwargs)\r
         else:\r
-            self._callable = lambda *args, **kwargs: fn(*args, **kwargs)\r
+            self._callable = fn\r
 \r
-    def _decorate_method(self, fn, needs_origin):\r
-        origin = self._callable\r
+    def _decorate_params(self, fn):\r
+        if not self._params_decoration:\r
+            self._params_decoration = []\r
 \r
-        if needs_origin:\r
-            self._callable = lambda inst, *args, **kwargs: fn(inst, origin, *args, **kwargs)\r
-        else:\r
-            self._callable = lambda inst, *args, **kwargs: fn(inst, *args, **kwargs)\r
+        self._params_decoration.append(fn)\r
 \r
+    def _decorate_result(self, fn):\r
+        if not self._result_decoration:\r
+            self._result_decoration = []\r
+\r
+        self._result_decoration.append(fn)\r
 \r
     def __call__(self, *args, **kwargs):\r
-        return self._callable(*args, **kwargs)\r
+        if self._params_decoration:\r
+            for dec in self._params_decoration:\r
+                args, kwargs = dec(*args, **kwargs)\r
+\r
+        res = self._callable(*args, **kwargs)\r
+\r
+        if self._result_decoration:\r
+            for dec in self._result_decoration:\r
+                res = dec(res)\r
 \r
+        return res\r
+\r
+\r
+def _create_decorator(origin, needs_origin, mode, method=False):\r
+    def decorator(fn):\r
+        if mode == DecoratableObject.MODE_OVERRIDE:\r
+            origin._decorate(fn, needs_origin, method=method)\r
+        elif mode == DecoratableObject.MODE_PARAMS:\r
+            origin._decorate_params(fn)\r
+        elif mode == DecoratableObject.MODE_RESULT:\r
+            origin._decorate_result(fn)\r
+\r
+        return fn\r
+\r
+    return decorator\r
 \r
-def _decorate_method(origin, needs_origin):\r
+\r
+def _decorate_method(origin, needs_origin, mode):\r
     if not hasattr(origin, '_decoratable_obj'):\r
         name = origin.__name__\r
         cls = origin.im_class\r
@@ -40,28 +77,36 @@ def _decorate_method(origin, needs_origin):
     else:\r
         decoratable = origin._decoratable_obj\r
 \r
-    def decorator(fn):\r
-        decoratable._decorate_method(fn, needs_origin)\r
-\r
-    return decorator\r
+    return _create_decorator(decoratable, needs_origin, mode, method=True)\r
 \r
-def _decorate_function(origin, needs_origin):\r
+def _decorate_function(origin, needs_origin, mode):\r
     if not isinstance(origin, DecoratableObject):\r
         mod = inspect.getmodule(origin)\r
 \r
         name = origin.__name__\r
         origin = DecoratableObject(origin)\r
-        setattr(mod, name, DecoratableObject(origin))\r
+        setattr(mod, name, origin)\r
+\r
+    return _create_decorator(origin, needs_origin, mode)\r
+\r
+\r
+def decorate(origin, needs_origin=True, mode=DecoratableObject.MODE_OVERRIDE):\r
+    if inspect.ismethod(origin):\r
+        return _decorate_method(origin, needs_origin, mode)\r
+\r
+    if inspect.isfunction(origin) or isinstance(origin, DecoratableObject):\r
+        return _decorate_function(origin, needs_origin, mode)\r
 \r
     def decorator(fn):\r
-        origin._decorate(fn, needs_origin)\r
+        return fn\r
 \r
-    return decorator\r
 \r
+def _decorate_params(origin):\r
+    return decorate(origin, mode=DecoratableObject.MODE_PARAMS)\r
 \r
-def decorate(origin, needs_origin=True):\r
-    if inspect.ismethod(origin):\r
-        return _decorate_method(origin, needs_origin)\r
+decorate.params = _decorate_params\r
+\r
+def _decorate_result(origin):\r
+    return decorate(origin, mode=DecoratableObject.MODE_RESULT)\r
 \r
-    if inspect.isfunction(origin):\r
-        return _decorate_function(origin, needs_origin)
\ No newline at end of file
+decorate.result = _decorate_result\r
diff --git a/forum/modules/ui.py b/forum/modules/ui.py
new file mode 100644 (file)
index 0000000..5cfcac4
--- /dev/null
@@ -0,0 +1,47 @@
+
+
+class Registry(list):
+    def add(self, register):
+        for i, r in enumerate(self):
+            if r.weight > register.weight:
+                self.insert(i, register)
+                return
+
+        self.append(register)
+
+"""Links next in the very top of the page"""
+HEADER_LINKS = 'HEADER_LINKS'
+
+"""The tabs next to the top of the page"""
+PAGE_TOP_TABS = 'PAGE_TOP_TABS'
+
+
+__CONTAINER = {
+    HEADER_LINKS: Registry(),
+    PAGE_TOP_TABS: Registry()
+}
+
+
+def register(registry, ui_object):
+    if not registry in __CONTAINER:
+        raise('unknown registry')
+
+    __CONTAINER[registry].add(ui_object)
+
+def register_multi(registry, *ui_objects):
+    for ui_object in ui_objects:
+        register(registry, ui_object)
+
+
+def get_registry_by_name(name):
+    name = name.upper()
+
+    if not name in __CONTAINER:
+        raise('unknown registry')
+
+    return __CONTAINER[name]
+
+
+
+from ui_objects import *
+
diff --git a/forum/modules/ui_objects.py b/forum/modules/ui_objects.py
new file mode 100644 (file)
index 0000000..01a61d3
--- /dev/null
@@ -0,0 +1,96 @@
+from django.core.urlresolvers import reverse
+from forum.utils import html
+
+class UiObjectUserLevelBase(object):
+    def show_to(self, user):
+        return True
+
+class SuperuserUiObject(UiObjectUserLevelBase):
+    def show_to(self, user):
+        return user.is_superuser
+
+class StaffUiObject(UiObjectUserLevelBase):
+    def show_to(self, user):
+        return user.is_staff or user.is_superuser
+
+class ReputedUserUiObject(UiObjectUserLevelBase):
+    def __init__(self, min_rep):
+        self.min_rep = min_rep
+
+    def show_to(self, user):
+        return user.is_authenticated() and user.reputation >= int(self.min_rep)
+
+class LoggedInUserUiObject(UiObjectUserLevelBase):
+    def show_to(self, user):
+        return user.is_authenticated()
+
+class PublicUiObject(UiObjectUserLevelBase):
+    pass
+
+
+
+class UiObjectArgument(object):
+    def __init__(self, argument):
+        self.argument = argument
+
+    def __call__(self, context):
+        if callable(self.argument):
+            return self.argument(context)
+        else:
+            return self.argument
+
+
+class UiObjectBase(object):
+    def __init__(self, user_level=None, weight=500):
+        self.user_level = user_level or PublicUiObject()
+        self.weight = weight
+
+    def can_render(self, context):
+        return self.user_level.show_to(context['request'].user)
+
+    def render(self, context):
+        return ''
+
+class UiLoopObjectBase(UiObjectBase):
+    def update_context(self, context):
+        pass
+
+
+
+class UiLinkObject(UiObjectBase):
+    def __init__(self, text, url, attrs=None, pre_code='', post_code='', user_level=None, weight=500):
+        super(UiLinkObject, self).__init__(user_level, weight)
+        self.text = UiObjectArgument(text)
+        self.url = UiObjectArgument(url)
+        self.attrs = UiObjectArgument(attrs or {})
+        self.pre_code = UiObjectArgument(pre_code)
+        self.post_code = UiObjectArgument(post_code)
+
+    def render(self, context):
+        return "%s %s %s" % (self.pre_code(context),
+            html.hyperlink(self.url(context), self.text(context), **self.attrs(context)),
+            self.post_code(context))
+
+
+class UiLoopContextObject(UiLoopObjectBase):
+    def __init__(self, loop_context, user_level=None, weight=500):
+        super(UiLoopContextObject, self).__init__(user_level, weight)
+        self.loop_context = UiObjectArgument(loop_context)
+
+    def update_context(self, context):
+        context.update(self.loop_context(context))
+
+
+class UiTopPageTabObject(UiLoopObjectBase):
+    def __init__(self, tab_name, tab_title, url_pattern, weight):
+        super(UiTopPageTabObject, self).__init__(weight=weight)
+        self.tab_name = tab_name
+        self.tab_title = tab_title
+        self.url_pattern = url_pattern
+
+    def update_context(self, context):
+        context.update(dict(
+            tab_name=self.tab_name,
+            tab_title=self.tab_title,
+            tab_url=reverse(self.url_pattern)
+        ))
index be6bd4f38e33055255d52ea788ba3b02dd27b840..7a7e0cdb7ca1d20624889f08c9586f55b857a117 100644 (file)
@@ -55,3 +55,6 @@ def find_media_source(url):
                 use_skin = ''
                 return None
     return use_skin + '/' + url
+
+
+
index 64056b61d15658bef3e9339218b3fc38b44d7c58..12a684a2f04542dc5e435e125c7eec2867eed955 100644 (file)
@@ -1,20 +1,10 @@
 <!-- template header.html -->
-{% load extra_tags %}
-{% load i18n %}
+{% load extra_tags ui_registry i18n %}
+
        <div id="roof">
                <div id="navBar">
                        <div id="top">
-                {% if request.user.is_authenticated %}
-                    {% if request.user.is_superuser %}
-                        <a href="{% url admin_index %}">{% trans "administration" %}</a>   
-                    {% endif %}
-                    <a href="{{ request.user.get_profile_url }}">{{ request.user.username }}</a> {% get_score_badge request.user %} 
-                    <a href="{% url logout %}">{% trans "logout" %}</a>
-                {% else %}
-                    <a href="{% url auth_signin %}">{% trans "login" %}</a>
-                {% endif %}
-                <a href="{% url about %}">{% trans "about" %}</a>
-                <a href="{% url faq %}">{% trans "faq" %}</a>
+                           {% loadregistry header_links %}
                        </div>
                        <table width="100%" border="0" cellspacing="0" cellpadding="0">
                          <tr>
                </td>
                                <td width="77%" valign="bottom">
                 <div class="nav">
-                    <a id="nav_questions"{% ifequal tab "questions" %} class="on"{% endifequal %} href="{% url questions %}" >{% trans "questions" %}</a>
-                    <a id="nav_tags"{% ifequal tab "tags" %} class="on"{% endifequal %} href="{% url tags %}">{% trans "tags" %}</a>
-                    <a id="nav_users"{% ifequal tab "users" %} class="on"{% endifequal %} href="{% url users %}">{% trans "users" %}</a>
-                    <a id="nav_badges"{% ifequal tab "badges" %} class="on"{% endifequal %} href="{% url badges %}">{% trans "badges" %}</a>
-                    <a id="nav_unanswered"{% ifequal tab "unanswered" %} class="on"{% endifequal %} href="{% url unanswered %}">{% trans "unanswered questions" %}</a>
+                    {% loopregistry page_top_tabs %}{% spaceless %}
+                        <a id="nav_{{ tab_name }}"{% ifequal tab tab_name %} class="on"{% endifequal %} href="{{ tab_url }}" >{{ tab_title }}</a>
+                    {% endspaceless %}{% endloopregistry %}
                     <div class="focus">
                     <a id="nav_ask" href="{% url ask %}" class="special">{% trans "ask a question" %}</a>
                     </div>
index 5740b6a3e7233ce849f95e6e45f0562c43f03933..4d605c3d03dae6d5bd045ae0049b272b26b71ea1 100644 (file)
@@ -2,12 +2,56 @@ import sys
 import os
 sys.path.append(os.path.join(os.path.dirname(__file__),'markdownext'))
 
-import forum.views
+
+from forum.modules import get_modules_script, ui
+
+get_modules_script('settings')
+get_modules_script('startup')
+
 
 import forum.badges
 import forum.subscriptions
 
-from forum.modules import get_modules_script
 
-get_modules_script('settings')
-get_modules_script('startup')
\ No newline at end of file
+from django.utils.translation import ugettext as _
+from django.core.urlresolvers import reverse
+from forum.templatetags.extra_tags import get_score_badge
+
+
+ui.register_multi(ui.HEADER_LINKS,
+            ui.UiLinkObject(_('faq'), 'faq', weight=400),
+            ui.UiLinkObject(_('about'), 'about', weight=300),
+
+            ui.UiLinkObject(
+                    text=lambda c: c['request'].user.is_authenticated() and _('logout') or _('login'),
+                    url=lambda c: c['request'].user.is_authenticated() and reverse('logout') or reverse('auth_signin'),
+                    weight=200),
+
+            ui.UiLinkObject(
+                    user_level=ui.LoggedInUserUiObject(),
+                    text=lambda c: c['request'].user.username,
+                    url=lambda c: c['request'].user.get_profile_url(),
+                    post_code=lambda c: get_score_badge(c['request'].user),
+                    weight=100),
+
+            ui.UiLinkObject(
+                    user_level=ui.SuperuserUiObject(),
+                    text=_('administration'),
+                    url=lambda c: reverse('admin_index'),
+                    weight=0)
+
+)
+
+
+ui.register_multi(ui.PAGE_TOP_TABS,
+            ui.UiTopPageTabObject('questions', _('questions'), 'questions', weight=0),
+            ui.UiTopPageTabObject('tags', _('tags'), 'tags', weight=100),
+            ui.UiTopPageTabObject('users', _('users'), 'users', weight=200),
+            ui.UiTopPageTabObject('badges', _('badges'), 'badges', weight=300),
+            ui.UiTopPageTabObject('unanswered', _('unanswered questions'), 'unanswered', weight=400),
+)
+
+#register.header_link(lambda c: (_('faq'), reverse('faq')))
+
+
+
diff --git a/forum/templatetags/ui_registry.py b/forum/templatetags/ui_registry.py
new file mode 100644 (file)
index 0000000..d91fd87
--- /dev/null
@@ -0,0 +1,58 @@
+from django import template
+from forum.modules import ui
+
+register = template.Library()
+
+
+class LoadRegistryNode(template.Node):
+    def __init__(self, registry):
+        self.registry = registry
+
+    def render(self, context):
+        result = ''
+
+        for ui_object in self.registry:
+            if ui_object.can_render(context):
+                result += ui_object.render(context)
+
+        return result
+
+
+@register.tag
+def loadregistry(parser, token):
+    try:
+        tag_name, registry = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
+
+    registry = ui.get_registry_by_name(registry)
+    return LoadRegistryNode(registry)
+
+
+class LoopRegistryNode(template.Node):
+    def __init__(self, registry, nodelist):
+        self.registry = registry
+        self.nodelist = nodelist
+
+    def render(self, context):
+        result = ''
+
+        for ui_object in self.registry:
+            if ui_object.can_render(context):
+                ui_object.update_context(context)
+                result += self.nodelist.render(context)
+
+        return result
+
+@register.tag
+def loopregistry(parser, token):
+    try:
+        tag_name, registry = token.split_contents()
+    except ValueError:
+        raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
+
+    registry = ui.get_registry_by_name(registry)
+    nodelist = parser.parse(('endloopregistry',))
+    parser.delete_first_token()
+
+    return LoopRegistryNode(registry, nodelist)
\ No newline at end of file
index e7ca42c8f4dda569f47813ebdf0f0b61753e5a40..d424382b9b150311d0ef4f392a56a8df9e6515be 100644 (file)
@@ -58,7 +58,7 @@ def html2text(s, ignore_tags=(), indent_width=4, page_width=80):
     return mark_safe(parser.result)
 
 def buildtag(name, content, **attrs):
-    return mark_safe('<%s %s>%s</a>' % (name, " ".join('%s="%s"' % i for i in attrs.items()), content))
+    return mark_safe('<%s %s>%s</a>' % (name, " ".join('%s="%s"' % i for i in attrs.items()), str(content)))
 
 def hyperlink(url, title, **attrs):
     return mark_safe('<a href="%s" %s>%s</a>' % (url, " ".join('%s="%s"' % i for i in attrs.items()), title))