]> git.openstreetmap.org Git - osqa.git/commitdiff
More polished PAI for module html injection and added a couple more places to inject...
authorhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Tue, 22 Jun 2010 13:13:26 +0000 (13:13 +0000)
committerhernani <hernani@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Tue, 22 Jun 2010 13:13:26 +0000 (13:13 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@440 0cfe37f9-358a-4d5e-be75-b63607b5c754

20 files changed:
forum/models/user.py
forum/modules/ui.py
forum/modules/ui_objects.py
forum/skins/default/templates/badges.html
forum/skins/default/templates/footer.html
forum/skins/default/templates/header.html
forum/skins/default/templates/notifications/base.html
forum/skins/default/templates/notifications/base_text.html
forum/skins/default/templates/subscription_status.html
forum/skins/default/templates/tags.html
forum/skins/default/templates/users/menu.html
forum/skins/default/templates/users/tabs.html
forum/skins/default/templates/users/users.html
forum/startup.py
forum/templatetags/ui_registry.py
forum/urls.py
forum/views/decorators.py
forum/views/meta.py
forum/views/readers.py
forum/views/users.py

index 8837ef35836a22d84dd92e248aea51408d0f13d3..89bea283824f67b75f7bb8d9a642b3769c14befd 100644 (file)
@@ -20,10 +20,6 @@ QUESTIONS_PER_PAGE_CHOICES = (
 (50, u'50'),
 )
 
-class UserManager(CachedManager):
-    def get_site_owner(self):
-        return self.all().order_by('date_joined')[0]
-
 class AnonymousUser(DjangoAnonymousUser):
     def get_visible_answers(self, question):
         return question.answers.filter_state(deleted=False)
@@ -115,11 +111,14 @@ class User(BaseModel, DjangoUser):
     vote_up_count = DenormalizedField("actions", canceled=False, action_type="voteup")
     vote_down_count = DenormalizedField("actions", canceled=False, action_type="votedown")
 
-    objects = UserManager()
-
     def __unicode__(self):
         return self.username
 
+    @property
+    def is_siteowner(self):
+        #temporary thing, for now lets just assume that the site owner will always be the first user of the application
+        return self.id == 1
+
     @property
     def gravatar(self):
         return md5(self.email).hexdigest()
index 5cfcac42b5d2347daa85cc552333a264c26c2785..e21527f9ecb890ff6b19807d9ffcda561e364b9c 100644 (file)
@@ -9,29 +9,27 @@ class Registry(list):
 
         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"""
+HEADER_LINKS = 'HEADER_LINKS'
 PAGE_TOP_TABS = 'PAGE_TOP_TABS'
+FOOTER_LINKS = 'FOOTER_LINKS'
+PROFILE_TABS = 'PROFILE_TABS'
 
 
 __CONTAINER = {
     HEADER_LINKS: Registry(),
-    PAGE_TOP_TABS: Registry()
+    PAGE_TOP_TABS: Registry(),
+    FOOTER_LINKS: Registry(),
+    PROFILE_TABS: Registry(),
 }
 
 
-def register(registry, ui_object):
+def register(registry, *ui_objects):
     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)
-
+        __CONTAINER[registry].add(ui_object)
 
 def get_registry_by_name(name):
     name = name.upper()
index 01a61d38c7ad9b8ff2a48d1241d61d3787e9c2a9..70bee93a1b695d69aa9325a93ad1668a127c29e9 100644 (file)
@@ -1,70 +1,81 @@
 from django.core.urlresolvers import reverse
+from django.template.defaultfilters import slugify
 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
+class Visibility(object):
+    def __init__(self, level='public'):
+        if level not in ['public', 'authenticated', 'staff', 'superuser', 'owner']:
+            try:
+                int(level)
+                self.by_reputation = True
+            except:
+                raise "Invalid visibility level for ui object: %s" % level
+        else:
+            self.by_reputation = False
 
-    def show_to(self, user):
-        return user.is_authenticated() and user.reputation >= int(self.min_rep)
+        self.level = level
 
-class LoggedInUserUiObject(UiObjectUserLevelBase):
     def show_to(self, user):
-        return user.is_authenticated()
-
-class PublicUiObject(UiObjectUserLevelBase):
-    pass
-
+        if self.by_reputation:
+            return user.is_authenticated() and (user.reputation >= int(self.level) or user.is_staff or user.is_superuser)
+        else:
+            return self.level == 'public' or (user.is_authenticated() and (
+                self.level == 'authenticated' or (
+                self.level == 'superuser' and user.is_superuser) or (
+                self.level == 'staff' and (user.is_staff or user.is_superuser)) or (
+                self.level == 'owner' and user.is_siteowner)))
+
+Visibility.PUBLIC = Visibility('public')
+Visibility.AUTHENTICATED = Visibility('authenticated')
+Visibility.STAFF = Visibility('staff')
+Visibility.SUPERUSER = Visibility('superuser')
+Visibility.OWNER = Visibility('owner')
+Visibility.REPUTED = lambda r: Visibility(r)
+
+
+class Url(object):
+    def __init__(self, url_pattern):
+        self.url_pattern = url_pattern
 
+    def __call__(self, u, c):
+        return reverse(self.url_pattern)
 
-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 ObjectBase(object):
+    class Argument(object):
+        def __init__(self, argument):
+            self.argument = argument
 
+        def __call__(self, context):
+            if callable(self.argument):
+                return self.argument(context['request'].user, context)
+            else:
+                return self.argument
 
-class UiObjectBase(object):
-    def __init__(self, user_level=None, weight=500):
-        self.user_level = user_level or PublicUiObject()
+    def __init__(self, visibility=None, weight=500):
+        self.visibility = visibility
         self.weight = weight
 
     def can_render(self, context):
-        return self.user_level.show_to(context['request'].user)
+        return (not self.visibility) or (self.visibility and self.visibility.show_to(context['request'].user))
 
     def render(self, context):
         return ''
 
-class UiLoopObjectBase(UiObjectBase):
+class LoopBase(ObjectBase):
     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)
+class Link(ObjectBase):
+    def __init__(self, text, url, attrs=None, pre_code='', post_code='', visibility=None, weight=500):
+        super(Link, self).__init__(visibility, weight)
+        self.text = self.Argument(text)
+        self.url = self.Argument(url)
+        self.attrs = self.Argument(attrs or {})
+        self.pre_code = self.Argument(pre_code)
+        self.post_code = self.Argument(post_code)
 
     def render(self, context):
         return "%s %s %s" % (self.pre_code(context),
@@ -72,25 +83,47 @@ class UiLinkObject(UiObjectBase):
             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)
+class LoopContext(LoopBase):
+    def __init__(self, loop_context, visibility=None, weight=500):
+        super(LoopContext, self).__init__(visibility, weight)
+        self.loop_context = self.Argument(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)
+class PageTab(LoopBase):
+    def __init__(self, tab_name, tab_title, url_getter, weight):
+        super(PageTab, self).__init__(weight=weight)
         self.tab_name = tab_name
         self.tab_title = tab_title
-        self.url_pattern = url_pattern
+        self.url_getter = url_getter
 
     def update_context(self, context):
         context.update(dict(
             tab_name=self.tab_name,
             tab_title=self.tab_title,
-            tab_url=reverse(self.url_pattern)
+            tab_url=self.url_getter()
         ))
+
+
+class ProfileTab(LoopBase):
+    def __init__(self, name, title, description, url_getter, private=False, weight=500):
+        super(ProfileTab, self).__init__(weight=weight)
+        self.name = name
+        self.title = title
+        self.description = description
+        self.url_getter = url_getter
+        self.private = private
+
+    def can_render(self, context):
+        return not self.private or (
+            context['view_user'] == context['request'].user or context['request'].user.is_superuser)
+
+    def update_context(self, context):        
+        context.update(dict(
+            tab_name=self.name,
+            tab_title=self.title,
+            tab_description = self.description,
+            tab_url=self.url_getter(context['view_user'])
+        ))
\ No newline at end of file
index e3a91e0cf7f9faa570e7ecdcae81bf7d2ddc1911..aaac3ad4fe8384d876418bc66915f1d8b414ef28 100644 (file)
@@ -4,14 +4,6 @@
 {% load humanize %}
 {% load i18n %}
 {% block title %}{% spaceless %}{% trans "Badges summary" %}{% endspaceless %}{% endblock %}
-{% block forejs %}
-       <script type="text/javascript">
-        $().ready(function(){
-            $("#nav_badges").attr('className',"on");
-        });
-         
-        </script>
-{% endblock %}
 {% block content %}
 <div class="headlineA">
         <span class="headMedals">{% trans "Badges" %}</span>
index 4e1b97ba500ead6d26c7e9f49bb7dd5ed59cc869..e9c9b516bea6309ef8c40052dab3f0ed8c462e1d 100644 (file)
@@ -1,26 +1,9 @@
-{% load extra_tags %}
-{% load i18n %}
+{% load extra_tags ui_registry i18n %}
 
 
 <div>
     <div class="footerLinks" >
-        <a href="{% url about %}">{% trans "about" %}</a><span class="link-separator"> |</span>
-        <a href="{% url faq %}">{% trans "faq" %}</a><span class="link-separator"> |</span>
-        <a href="{% url privacy %}">{% trans "privacy" %}</a><span class="link-separator"> |</span>
-        {% if settings.SUPPORT_URL %}
-            <a href="{{settings.SUPPORT_URL}}" target="_blank">{% trans "support" %}</a><span class="link-separator"> |</span>
-        {% endif %}
-        {% spaceless %}
-        <a href=
-            {% if settings.CONTACT_URL %}
-                "{{settings.CONTACT_URL}}"
-                target="_blank">
-            {% else %}
-                "{% url feedback %}?next={{request.path}}">
-            {% endif %}
-            {% trans "contact us" %}
-        </a>
-        {% endspaceless %}
+        {% loadregistry footer_links %}<span class="link-separator"> |</span>{% endloadregistry %}
     </div>
   <p>
      <a href="http://osqa.net" target="_blank" title="OSQA {{ settings.OSQA_VERSION }} ({{ settings.SVN_REVISION }})">
index 12a684a2f04542dc5e435e125c7eec2867eed955..5710550f8631981caaf95439845d7cae8b0f9887 100644 (file)
@@ -4,7 +4,7 @@
        <div id="roof">
                <div id="navBar">
                        <div id="top">
-                           {% loadregistry header_links %}
+                           {% loadregistry header_links %}{% endloadregistry %}
                        </div>
                        <table width="100%" border="0" cellspacing="0" cellpadding="0">
                          <tr>
index 90a06849cc16ae9302030ef4bba1c6d99102c8cd..cd7e69c236db1f750d8260a3be99fc5a8abdd997 100644 (file)
@@ -23,7 +23,7 @@
 <p style="{{ p_style }}">{% trans "Thanks" %},<br />{{settings.APP_SHORT_NAME}}</p>
 {% if not exclude_finetune %}
 <p style="{{ p_style }}">{% trans "P.S. You can always fine-tune which notifications you receive" %}
-<a href="{{ settings.APP_URL }}{% url user_subscriptions id=recipient.id %}" style="{{ a_style }}">{% trans "here" %}</a>.
+<a href="{{ settings.APP_URL }}{% url user_subscriptions id=recipient.id,slug=recipient.username|slugify %}" style="{{ a_style }}">{% trans "here" %}</a>.
 {% endif %}
 </p>
 <hr style="{{ hr_style }}" />
index 113e5fdf6535eba325254da26be2e49557b681cd..8cd2c4662d66921de4ffe7fc8ad15eef60ad2d14 100644 (file)
@@ -14,7 +14,7 @@
 
 {% if not exclude_finetune %}
 {% trans "P.S. You can always fine-tune which notifications you receive here:" %}
-{{ settings.APP_URL }}{% url user_subscriptions id=recipient.id %}
+{{ settings.APP_URL }}{% url user_subscriptions id=recipient.id,slug=recipient.username|slugify %}
 {% endif %}
 
 {{ postal_address }}
\ No newline at end of file
index 6539ddd95798f2dfdaf1b8ff740866ce14aa4f58..c489da1922dc13e21026d828441a3ad2b7d44127 100644 (file)
@@ -21,7 +21,7 @@
         {% endif %}\r
     </a></p>\r
     <p>\r
-        {% url user_subscriptions id=request.user.id as subscriptions_url %}\r
+        {% url user_subscriptions id=request.user.id,slug=request.user.username|slugify as subscriptions_url %}\r
         {% blocktrans %}\r
             (you can adjust your notification settings on your <a href="{{ subscriptions_url }}">profile</a>)\r
         {% endblocktrans %}\r
index 50f90fb19c8b2aee81054a93fa2789e53cfb074f..16ec11abfd32f6d6c8ac1ccd132435c7f90f4e1d 100644 (file)
@@ -8,7 +8,6 @@
        <script type="text/javascript">
        /*<![CDATA[*/
         $().ready(function(){
-            $("#nav_tags").attr('className',"on");
             $("#ipSearchTag").focus();
 
             var orderby = "{{ tab_id }}";
index 18b886f4426a23253f8e334e393f0fcbb53ce71c..650c41f4e996d09661775ba05086b44c2cc20bee 100644 (file)
@@ -10,7 +10,7 @@
     <ul id="user-menu-dropdown">\r
         <li class="item"><span class="user-edit"></span><a href="{% url edit_user id=user.id %}">{% trans "edit profile" %}</a></li>\r
         <li class="item"><span class="user-auth"></span><a href="{% url user_authsettings id=user.id %}">{% trans "authentication settings" %}</a></li>\r
-        <li class="item"><span class="user-subscriptions"></span><a href="{% url user_subscriptions id=user.id %}">{% trans "email notification settings" %}</a></li>\r
+        <li class="item"><span class="user-subscriptions"></span><a href="{% url user_subscriptions id=user.id,slug=user.username|slugify %}">{% trans "email notification settings" %}</a></li>\r
         {% ifnotequal user viewer %}\r
             {% if viewer.is_superuser %}\r
             <li class="separator">{% trans "Moderation tools" %}</li>\r
                         <a class="confirm" href="{% url user_powers id=user.id,action="grant",status="super" %}">{% trans "grant super user status" %}</a>\r
                     </li>\r
                 {% else %}\r
-                    {% ifequal viewer.id 1 %}\r
+                    {% if viewer.is_siteowner %}\r
                     <li class="item"><span class="user-superuser"></span>\r
                         <a class="confirm" href="{% url user_powers id=user.id,action="remove",status="super" %}">{% trans "remove super user status" %}</a>\r
                     </li>\r
-                    {% endifequal %}\r
+                    {% endif %}\r
                 {% endif %}\r
             {% endif %}\r
         {% endifnotequal %}\r
index 1815c24cefa72846810a28ee59565940f495907f..d3d8d90a560509414986edea17cf99cca5c037e8 100644 (file)
@@ -1,22 +1,12 @@
-{% load extra_filters %}
-{% load i18n %}
+{% load extra_filters ui_registry i18n %}
 {% with view_user.username|slugify as user_slug %}
 <div class="tabBar">
     <div class="tabsA">
-        <a id="stats" {% ifequal tab_name "stats" %}class="on"{% endifequal %} 
-                       title="{% trans "User profile" %}" href="{% url user_profile id=view_user.id,slug=user_slug %}">{% trans "overview" %}</a>
-        <a id="recent" {% ifequal tab_name "recent" %}class="on"{% endifequal %} 
-                       title="{% trans "recent activity" %}" href="{% url user_recent id=view_user.id,slug=user_slug %}">{% trans "recent activity" %}</a>
-        <a id="reputation" {% ifequal tab_name "reputation" %}class="on"{% endifequal %}
-                       title="{% trans "graph of user reputation" %}" 
-                       href="{% url user_reputation id=view_user.id,slug=user_slug %}">{% trans "reputation history" %}</a>
-        {% if can_view_private %}
-        <a id="votes" {% ifequal tab_name "votes" %}class="on"{% endifequal %} 
-                       title="{% trans "user vote record" %}" href="{% url user_votes id=view_user.id,slug=user_slug %}">{% trans "casted votes" %}</a>
-        {% endif %}
-        <a id="favorites" {% ifequal tab_name "favorites" %}class="on"{% endifequal %} 
-                       title="{% trans "questions that user selected as his/her favorite" %}"
-                       href="{% url user_favorites id=view_user.id,slug=user_slug %}">{% trans "favorites" %}</a>
+        {% loopregistry profile_tabs %}{% spaceless %}
+            <a title="{{ tab_description }}" id="{{ tab_name }}" {% ifequal active_tab tab_name %}class="on"{% endifequal %} href="{{ tab_url }}">
+                {{ tab_title }}
+            </a>
+        {% endspaceless %}{% endloopregistry %}
     </div>
 </div>
 {% endwith %}
index a79058d8e877e00fc5da06d7b416b6019e4cd602..38f060e257413fd3a329e68a33fb425389770e91 100644 (file)
@@ -8,7 +8,6 @@
        <script type="text/javascript">
           //todo move javascript out
         $().ready(function(){
-            $("#nav_users").attr('className',"on");
             $("#type-user").attr('checked',true);
             var orderby = "{{ tab_id }}";
             $("#sort_" + orderby).attr('className',"on");
index 4d605c3d03dae6d5bd045ae0049b272b26b71ea1..d203ab2dfec0e0e8e0beb85008504566f772cb90 100644 (file)
@@ -16,42 +16,50 @@ import forum.subscriptions
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
 from forum.templatetags.extra_tags import get_score_badge
+from forum import settings
 
 
-ui.register_multi(ui.HEADER_LINKS,
-            ui.UiLinkObject(_('faq'), 'faq', weight=400),
-            ui.UiLinkObject(_('about'), 'about', weight=300),
+ui.register(ui.HEADER_LINKS,
+            ui.Link(_('faq'), ui.Url('faq'), weight=400),
+            ui.Link(_('about'), ui.Url('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'),
+            ui.Link(
+                    text=lambda u, c: u.is_authenticated() and _('logout') or _('login'),
+                    url=lambda u, c: u.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),
+            ui.Link(
+                    visibility=ui.Visibility.AUTHENTICATED,
+                    text=lambda u, c: u.username,
+                    url=lambda u, c: u.get_profile_url(),
+                    post_code=lambda u, c: get_score_badge(u),
                     weight=100),
 
-            ui.UiLinkObject(
-                    user_level=ui.SuperuserUiObject(),
+            ui.Link(
+                    visibility=ui.Visibility.SUPERUSER,
                     text=_('administration'),
-                    url=lambda c: reverse('admin_index'),
+                    url=lambda u, c: reverse('admin_index'),
                     weight=0)
 
 )
 
+class SupportLink(ui.Link):    
+    def can_render(self, context):
+        return bool(settings.SUPPORT_URL)
 
-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),
+
+ui.register(ui.FOOTER_LINKS,
+            ui.Link(
+                    text=_('contact'),
+                    url=lambda u, c: settings.CONTACT_URL and settings.CONTACT_URL or "%s?next=%s" % (reverse('feedback'), c['request'].path),
+                    weight=400),
+            SupportLink(_('support'), settings.SUPPORT_URL, attrs={'target': '_blank'}, weight=300),
+            ui.Link(_('privacy'), ui.Url('privacy'), weight=200),
+            ui.Link(_('faq'), ui.Url('faq'), weight=100),
+            ui.Link(_('about'), ui.Url('about'), weight=0),
 )
 
-#register.header_link(lambda c: (_('faq'), reverse('faq')))
+
 
 
 
index d91fd87e6f84c338dfa90416a0ab8ca5fec24ca5..6627b2b71852b87917b096a19defef34324c226e 100644 (file)
@@ -5,14 +5,18 @@ register = template.Library()
 
 
 class LoadRegistryNode(template.Node):
-    def __init__(self, registry):
+    def __init__(self, registry, separator):
         self.registry = registry
+        self.separator = separator
 
     def render(self, context):
+        separator = self.separator.render(context)
         result = ''
 
         for ui_object in self.registry:
             if ui_object.can_render(context):
+                if result:
+                    result += separator
                 result += ui_object.render(context)
 
         return result
@@ -26,7 +30,9 @@ def loadregistry(parser, token):
         raise template.TemplateSyntaxError, "%r tag requires exactly one argument" % token.contents.split()[0]
 
     registry = ui.get_registry_by_name(registry)
-    return LoadRegistryNode(registry)
+    separator = parser.parse(('endloadregistry',))
+    parser.delete_first_token()
+    return LoadRegistryNode(registry, separator)
 
 
 class LoopRegistryNode(template.Node):
index b92311e800d9e20274597c59dc1c4bfc2929923e..6bb315dbdc2b927e550788416725275e7136ba4b 100644 (file)
@@ -118,7 +118,7 @@ urlpatterns += patterns('',
                             ),
                         url(r'^%s(?P<id>\d+)/%s(?P<action>[a-z]+)/(?P<status>[a-z]+)/$' % (_('users/'), _('powers/')),
                             app.users.user_powers, name='user_powers'),
-                        url(r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('subscriptions/')), app.users.user_subscriptions,
+                        url(r'^%s(?P<id>\d+)/(?P<slug>.+)/%s$' % (_('users/'), _('subscriptions/')), app.users.user_subscriptions,
                             name='user_subscriptions'),
                         url(r'^%s(?P<id>\d+)/(?P<slug>.+)/%s$' % (_('users/'), _('favorites/')),
                             app.users.user_favorites, name='user_favorites'),
@@ -128,7 +128,7 @@ urlpatterns += patterns('',
                             name='user_votes'),
                         url(r'^%s(?P<id>\d+)/(?P<slug>.+)/%s$' % (_('users/'), _('recent/')), app.users.user_recent,
                             name='user_recent'),
-                        url(r'^%s(?P<id>\d+)/(?P<slug>.+)/$' % _('users/'), app.users.user_stats, name='user_profile'),
+                        url(r'^%s(?P<id>\d+)/(?P<slug>.+)/$' % _('users/'), app.users.user_profile, name='user_profile'),
 
                         url(r'^%s$' % _('badges/'), app.meta.badges, name='badges'),
                         url(r'^%s(?P<id>\d+)/(?P<slug>.+)$' % _('badges/'), app.meta.badge, name='badge'),
index db6dec48e9712f194cac483bdf74e5c49a72c6a2..bae9712332e50a4dce652b0b6b1f996d54326c8f 100644 (file)
@@ -2,11 +2,13 @@ from django.http import HttpResponse, HttpResponseRedirect, Http404
 from django.utils import simplejson\r
 from django.core.paginator import Paginator, EmptyPage\r
 from django.shortcuts import render_to_response\r
+from django.core.urlresolvers import reverse\r
 from django.template import RequestContext\r
 from django.utils.translation import ungettext, ugettext as _\r
+from forum.modules import ui\r
 import logging\r
 \r
-def render(template=None, tab=None):\r
+def render(template=None, tab=None, tab_title='', weight=500, tabbed=True):\r
     def decorator(func):\r
         def decorated(request, *args, **kwargs):\r
             context = func(request, *args, **kwargs)\r
@@ -17,6 +19,10 @@ def render(template=None, tab=None):
             return render_to_response(context.pop('template', template), context,\r
                                       context_instance=RequestContext(request))\r
 \r
+        if tabbed and tab:\r
+            ui.register(ui.PAGE_TOP_TABS,\r
+                        ui.PageTab(tab, tab_title, lambda: reverse(func.__name__), weight=weight))\r
+            \r
         return decorated\r
 \r
     return decorator\r
index 932c627a123d6b9637707395ccd3a6a86b96468a..8195e44f896b1478c4d8a5585c3048c9b93448a2 100644 (file)
@@ -17,6 +17,7 @@ from forum import settings
 from forum.utils.mail import send_template_email
 from django.utils.safestring import mark_safe
 from forum.templatetags.extra_filters import or_preview
+import decorators
 import re
 
 def favicon(request):
@@ -74,6 +75,7 @@ def logout(request):
     'next' : get_next_url(request),
     }, context_instance=RequestContext(request))
 
+@decorators.render('badges.html', 'badges', _('badges'), weight=300)
 def badges(request):
     badges = [b.ondb for b in sorted(BadgesMeta.by_id.values(), lambda b1, b2: cmp(b1.name, b2.name))]
 
@@ -82,10 +84,10 @@ def badges(request):
     else:
         my_badges = []
 
-    return render_to_response('badges.html', {
-    'badges' : badges,
-    'mybadges' : my_badges,
-    }, context_instance=RequestContext(request))
+    return {
+        'badges' : badges,
+        'mybadges' : my_badges,
+    }
 
 def badge(request, id, slug):
     badge = Badge.objects.get(id=id)
index 860f54c51f25d3a5efdb02df1ed4ab1f4514a1ee..3d353f184552bdbb1ef19c7702f2b5395ed5ffdc 100644 (file)
@@ -47,7 +47,7 @@ def index(request):
                          sort=request.utils.set_sort_method('active'),
                          base_path=reverse('questions'))
 
-@decorators.render('questions.html', 'unanswered')
+@decorators.render('questions.html', 'unanswered', _('unanswered'), weight=400)
 def unanswered(request):
     return question_list(request,
                          Question.objects.filter(extra_ref=None),
@@ -56,7 +56,7 @@ def unanswered(request):
                          None,
                          _("Unanswered Questions"))
 
-@decorators.render('questions.html', 'questions')
+@decorators.render('questions.html', 'questions', _('questions'), weight=0)
 def questions(request):
     return question_list(request, Question.objects.all(), _('questions'), request.utils.set_sort_method('active'))
 
@@ -145,7 +145,8 @@ def question_search(request, keywords):
                          _("questions matching '%(keywords)s'") % {'keywords': keywords})
 
 
-def tags(request):#view showing a listing of available tags - plain list
+@decorators.render('tags.html', 'tags', _('tags'), weight=100)
+def tags(request):
     stag = ""
     is_paginated = True
     sortby = request.GET.get('sort', 'used')
@@ -169,22 +170,22 @@ def tags(request):#view showing a listing of available tags - plain list
     except (EmptyPage, InvalidPage):
         tags = objects_list.page(objects_list.num_pages)
 
-    return render_to_response('tags.html', {
-    "tags" : tags,
-    "stag" : stag,
-    "tab_id" : sortby,
-    "keywords" : stag,
-    "context" : {
-    'is_paginated' : is_paginated,
-    'pages': objects_list.num_pages,
-    'page': page,
-    'has_previous': tags.has_previous(),
-    'has_next': tags.has_next(),
-    'previous': tags.previous_page_number(),
-    'next': tags.next_page_number(),
-    'base_url' : reverse('tags') + '?sort=%s&' % sortby
+    return {
+        "tags" : tags,
+        "stag" : stag,
+        "tab_id" : sortby,
+        "keywords" : stag,
+        "context" : {
+            'is_paginated' : is_paginated,
+            'pages': objects_list.num_pages,
+            'page': page,
+            'has_previous': tags.has_previous(),
+            'has_next': tags.has_next(),
+            'previous': tags.previous_page_number(),
+            'next': tags.next_page_number(),
+            'base_url' : reverse('tags') + '?sort=%s&' % sortby
+        }
     }
-    }, context_instance=RequestContext(request))
 
 def get_answer_sort_order(request):
     view_dic = {"latest":"-added_at", "oldest":"added_at", "votes":"-score" }
index 9978255dfe4c2ab0574d763d285c7dadb04cbaf9..6cc079bd14205a2e688c2473ec04b2f38a2c3eef 100644 (file)
@@ -13,17 +13,20 @@ from django.utils.translation import ugettext as _
 from django.utils.http import urlquote_plus\r
 from django.utils.html import strip_tags\r
 from django.utils import simplejson\r
-from django.core.urlresolvers import reverse\r
+from django.core.urlresolvers import reverse, NoReverseMatch\r
 from forum.forms import *\r
 from forum.utils.html import sanitize_html\r
 from datetime import datetime, date\r
 import decorators\r
 from forum.actions import EditProfileAction, FavoriteAction, BonusRepAction, SuspendAction\r
+from forum.modules import ui\r
 \r
 import time\r
+import decorators\r
 \r
 USERS_PAGE_SIZE = 35# refactor - move to some constants file\r
 \r
+@decorators.render('users/users.html', 'users', _('users'), weight=200)\r
 def users(request):\r
     is_paginated = True\r
     sortby = request.GET.get('sort', 'reputation')\r
@@ -55,24 +58,23 @@ def users(request):
     except (EmptyPage, InvalidPage):\r
         users = objects_list.page(objects_list.num_pages)\r
 \r
-    return render_to_response('users/users.html', {\r
-    "users" : users,\r
-    "suser" : suser,\r
-    "keywords" : suser,\r
-    "tab_id" : sortby,\r
-    "context" : {\r
-    'is_paginated' : is_paginated,\r
-    'pages': objects_list.num_pages,\r
-    'page': page,\r
-    'has_previous': users.has_previous(),\r
-    'has_next': users.has_next(),\r
-    'previous': users.previous_page_number(),\r
-    'next': users.next_page_number(),\r
-    'base_url' : base_url\r
+    return {\r
+        "users" : users,\r
+        "suser" : suser,\r
+        "keywords" : suser,\r
+        "tab_id" : sortby,\r
+        "context" : {\r
+            'is_paginated' : is_paginated,\r
+            'pages': objects_list.num_pages,\r
+            'page': page,\r
+            'has_previous': users.has_previous(),\r
+            'has_next': users.has_next(),\r
+            'previous': users.previous_page_number(),\r
+            'next': users.next_page_number(),\r
+            'base_url' : base_url\r
+        }\r
     }\r
 \r
-    }, context_instance=RequestContext(request))\r
-\r
 def set_new_email(user, new_email, nomessage=False):\r
     if new_email != user.email:\r
         user.email = new_email\r
@@ -185,7 +187,7 @@ def suspend(request, id):
 \r
     return decorators.RefreshPageCommand()\r
 \r
-def user_view(template, tab_name, tab_description, page_title, private=False):\r
+def user_view(template, tab_name, tab_title, tab_description, private=False, tabbed=True, weight=500):\r
     def decorator(fn):\r
         def decorated(request, id, slug=None):\r
             user = get_object_or_404(User, id=id)\r
@@ -193,23 +195,34 @@ def user_view(template, tab_name, tab_description, page_title, private=False):
                 return HttpResponseUnauthorized(request)\r
             context = fn(request, user)\r
 \r
-            rev_page_title = user.username + " - " + page_title\r
+            rev_page_title = user.username + " - " + tab_description\r
 \r
             context.update({\r
-            "tab_name" : tab_name,\r
+            "active_tab" : tab_name,\r
             "tab_description" : tab_description,\r
             "page_title" : rev_page_title,\r
             "can_view_private": (user == request.user) or request.user.is_superuser\r
             })\r
             return render_to_response(template, context, context_instance=RequestContext(request))\r
 \r
+        if tabbed:\r
+            def url_getter(vu):\r
+                try:\r
+                    return reverse(fn.__name__, kwargs={'id': vu.id, 'slug': slugify(vu.username)})\r
+                except NoReverseMatch:\r
+                    return reverse(fn.__name__, kwargs={'id': vu.id})\r
+\r
+            ui.register(ui.PROFILE_TABS, ui.ProfileTab(\r
+                tab_name, tab_title, tab_description,url_getter, private, weight\r
+            ))\r
+\r
         return decorated\r
 \r
     return decorator\r
 \r
 \r
-@user_view('users/stats.html', 'stats', _('user profile'), _('user overview'))\r
-def user_stats(request, user):\r
+@user_view('users/stats.html', 'stats', _('overview'), _('user overview'))\r
+def user_profile(request, user):\r
     questions = Question.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
     answers = Answer.objects.filter_state(deleted=False).filter(author=user).order_by('-added_at')\r
 \r
@@ -238,7 +251,7 @@ def user_stats(request, user):
     "total_awards" : len(awards),\r
     }\r
 \r
-@user_view('users/recent.html', 'recent', _('recent user activity'), _('recent activity'))\r
+@user_view('users/recent.html', 'recent', _('recent activity'), _('recent user activity'))\r
 def user_recent(request, user):\r
     activities = user.actions.exclude(\r
             action_type__in=("voteup", "votedown", "voteupcomment", "flag", "newpage", "editpage")).order_by(\r
@@ -247,15 +260,7 @@ def user_recent(request, user):
     return {"view_user" : user, "activities" : activities}\r
 \r
 \r
-@user_view('users/votes.html', 'votes', _('user vote record'), _('votes'), True)\r
-def user_votes(request, user):\r
-    votes = user.votes.exclude(node__state_string__contains="(deleted").filter(\r
-            node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]\r
-\r
-    return {"view_user" : user, "votes" : votes}\r
-\r
-\r
-@user_view('users/reputation.html', 'reputation', _('user reputation in the community'), _('user reputation'))\r
+@user_view('users/reputation.html', 'reputation', _('karma history'), _('graph of user karma'))\r
 def user_reputation(request, user):\r
     rep = list(user.reputes.order_by('date'))\r
     values = [r.value for r in rep]\r
@@ -270,13 +275,20 @@ def user_reputation(request, user):
 \r
     return {"view_user": user, "reputation": rep, "graph_data": graph_data}\r
 \r
-@user_view('users/questions.html', 'favorites', _('favorite questions'), _('favorite questions'))\r
+@user_view('users/votes.html', 'votes', _('votes'), _('user vote record'), True)\r
+def user_votes(request, user):\r
+    votes = user.votes.exclude(node__state_string__contains="(deleted").filter(\r
+            node__node_type__in=("question", "answer")).order_by('-voted_at')[:USERS_PAGE_SIZE]\r
+\r
+    return {"view_user" : user, "votes" : votes}\r
+\r
+@user_view('users/questions.html', 'favorites', _('favorites'), _('questions that user selected as his/her favorite'))\r
 def user_favorites(request, user):\r
     favorites = FavoriteAction.objects.filter(canceled=False, user=user)\r
 \r
     return {"favorites" : favorites, "view_user" : user}\r
 \r
-@user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True)\r
+@user_view('users/subscriptions.html', 'subscriptions', _('subscription settings'), _('subscriptions'), True, tabbed=False)\r
 def user_subscriptions(request, user):\r
     if request.method == 'POST':\r
         form = SubscriptionSettingsForm(request.POST)\r