incubator-allura-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From john...@apache.org
Subject [10/28] git commit: [#7002] Show activity on user profile
Date Thu, 23 Jan 2014 17:29:52 GMT
[#7002] Show activity on user profile

Signed-off-by: Cory Johns <cjohns@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/cae66cc4
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/cae66cc4
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/cae66cc4

Branch: refs/heads/cj/4257
Commit: cae66cc466d698aded0f83514d0eceb850e20d08
Parents: be272fa
Author: Cory Johns <cjohns@slashdotmedia.com>
Authored: Wed Jan 15 21:37:51 2014 +0000
Committer: Tim Van Steenburgh <tvansteenburgh@gmail.com>
Committed: Wed Jan 22 14:26:21 2014 +0000

----------------------------------------------------------------------
 Allura/allura/ext/user_profile/__init__.py      |  2 +-
 .../ext/user_profile/templates/user_index.html  |  4 ++
 Allura/allura/ext/user_profile/user_main.py     | 41 ++++++++++++++-
 Allura/allura/nf/allura/css/allura.css          |  1 +
 Allura/allura/nf/allura/css/site_style.css      | 53 ++++++++++++++++++++
 .../tests/functional/test_user_profile.py       | 30 +++++++++++
 ForgeActivity/forgeactivity/main.py             | 28 +++++++++++
 .../forgeactivity/templates/index.html          | 20 ++------
 .../forgeactivity/templates/macros.html         | 33 ++++++++++++
 .../templates/widgets/profile_section.html      | 51 +++++++++++++++++++
 ForgeActivity/setup.py                          |  3 ++
 11 files changed, 247 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/ext/user_profile/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/__init__.py b/Allura/allura/ext/user_profile/__init__.py
index 2657c92..5b323d6 100644
--- a/Allura/allura/ext/user_profile/__init__.py
+++ b/Allura/allura/ext/user_profile/__init__.py
@@ -15,4 +15,4 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-from .user_main import UserProfileApp
+from .user_main import UserProfileApp, ProfileSectionBase

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/ext/user_profile/templates/user_index.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/templates/user_index.html b/Allura/allura/ext/user_profile/templates/user_index.html
index 41f7f67..e567095 100644
--- a/Allura/allura/ext/user_profile/templates/user_index.html
+++ b/Allura/allura/ext/user_profile/templates/user_index.html
@@ -290,6 +290,10 @@
         </ul>
       </div>
   {% endif %}
+
+  {% for section in sections %}
+    {{ section.display() }}
+  {% endfor %}
 {% endblock %}
 
 {% block extra_js %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/ext/user_profile/user_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index 9198696..530d5a3 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -16,13 +16,18 @@
 #       under the License.
 
 import logging
+import re
 
 import pkg_resources
 from pylons import tmpl_context as c
+from pylons import app_globals as g
 from pylons import request
 from formencode import validators
 from tg import expose, redirect, validate, flash
+import tg
 from webob import exc
+from ming.utils import LazyProperty
+from jinja2 import Markup
 
 from allura import version
 from allura.app import Application, SitemapEntry
@@ -83,6 +88,18 @@ class UserProfileApp(Application):
     def uninstall(self, project):  # pragma no cover
         pass
 
+    @LazyProperty
+    def profile_sections(self):
+        sections = {}
+        for ep in h.iter_entry_points('allura.user_profile.sections'):
+            sections[ep.name] = ep.load()
+        section_ordering = tg.config.get('user_profile_sections.order', '')
+        ordered_sections = []
+        for section in re.split(r'\s*,\s*', section_ordering):
+            if section in sections:
+                ordered_sections.append(sections.pop(section))
+        return ordered_sections + sections.values()
+
 
 class UserProfileController(BaseController, FeedController):
 
@@ -114,7 +131,12 @@ class UserProfileController(BaseController, FeedController):
         if not user:
             raise exc.HTTPNotFound()
         provider = AuthenticationProvider.get(request)
-        return dict(user=user, reg_date=provider.user_registration_date(user))
+        sections = [section(user, c.project) for section in c.app.profile_sections]
+        return dict(
+                user=user,
+                reg_date=provider.user_registration_date(user),
+                sections=sections,
+            )
 
     def get_feed(self, project, app, user):
         """Return a :class:`allura.controllers.feed.FeedArgs` object describing
@@ -162,3 +184,20 @@ class UserProfileController(BaseController, FeedController):
                 c.user.user_message_max_messages,
                 c.user.user_message_time_interval), 'error')
         return redirect(c.project.user_project_of.url())
+
+
+class ProfileSectionBase(object):
+    template = ''
+
+    def check_display(self):
+        return True
+
+    def prepare_context(self, context):
+        return context
+
+    def display(self, *a, **kw):
+        if not self.check_display():
+            return ''
+        tmpl = g.jinja2_env.get_template(self.template)
+        context = self.prepare_context({'h': h, 'c': c})
+        return Markup(tmpl.render(context))

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/nf/allura/css/allura.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/allura.css b/Allura/allura/nf/allura/css/allura.css
index 628fe61..ddf27cc 100644
--- a/Allura/allura/nf/allura/css/allura.css
+++ b/Allura/allura/nf/allura/css/allura.css
@@ -53,6 +53,7 @@ b.ico.ico-feed { background-position: -16px -176px; }
 b.ico.ico-close { background-position: -80px -128px; }
 b.ico.ico-vote-up { background-image: url('../images/vote_up.png'); }
 b.ico.ico-vote-down { background-image: url('../images/vote_down.png'); }
+b.ico.ico-star { background-position: -240px -96px; }
 
 
 #ticket_search_results_holder{

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/nf/allura/css/site_style.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/site_style.css b/Allura/allura/nf/allura/css/site_style.css
index 05f1d05..6c6ff02 100644
--- a/Allura/allura/nf/allura/css/site_style.css
+++ b/Allura/allura/nf/allura/css/site_style.css
@@ -3240,3 +3240,56 @@ ul.dropdown ul li a:hover {
 .content-maximized #restore-content {
     display: inline;
 }
+
+.user-activity {
+    position: absolute;
+    top: 60px;
+    right: 0px;
+    border: 1px solid #ccc;
+    border-radius: 4px;
+}
+.user-activity h3 {
+    background-color: #555555;
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0%, #666666), color-stop(100%,
#555555));
+    background-image: -moz-linear-gradient(top, #666666 0%, #555555 100%);
+    background-image: linear-gradient(top, #666666 0%, #555555 100%);
+    border: 1px solid #333333;
+    color: #fff;
+    padding: 10px;
+    font-weight: normal;
+    font-size: 14px;
+}
+.user-activity h3 a {
+    float: right;
+}
+.user-activity h3 b.ico {
+    background-image: url('../images/neo-icon-set-ffffff-256x350.png');
+}
+.user-activity .empty {
+    padding: 10px;
+    font-style: italic;
+}
+.user-activity ul {
+    margin: 0px;
+    list-style: none;
+}
+.user-activity ul li {
+    padding: 10px;
+    border-bottom: 1px solid #ccc;
+}
+.user-activity ul li img {
+    vertical-align: text-bottom;
+}
+.user-activity ul li p {
+    padding: 5px 0 0 0;
+}
+.user-activity ul li time {
+    display: block;
+    text-align: right;
+    font-size: 10px;
+}
+.user-activity a.view-all {
+    display: block;
+    text-align: right;
+    padding: 5px 10px 5px 0;
+}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/Allura/allura/tests/functional/test_user_profile.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_user_profile.py b/Allura/allura/tests/functional/test_user_profile.py
index 3beb105..71ae05a 100644
--- a/Allura/allura/tests/functional/test_user_profile.py
+++ b/Allura/allura/tests/functional/test_user_profile.py
@@ -16,6 +16,8 @@
 #       under the License.
 
 import mock
+import tg
+from nose.tools import assert_equal, assert_in, assert_not_in
 
 from allura.model import Project, User
 from allura.tests import decorators as td
@@ -149,3 +151,31 @@ class TestUserProfile(TestController):
         r = self.app.get('/u/test-user/profile/send_message', status=302)
         assert 'This user has disabled direct email messages' in self.webflash(
             r)
+
+    @td.with_user_project('test-user')
+    def test_profile_sections(self):
+        project = Project.query.get(shortname='u/test-user')
+        app = project.app_instance('profile')
+        def ep(n):
+            m = mock.Mock()
+            m.name = n
+            m.load()().display.return_value = 'Section %s' % n
+            return m
+        eps = map(ep, ['a', 'b', 'c', 'd'])
+        order = {'user_profile_sections.order': 'b, d,c , f '}
+        with mock.patch('allura.lib.helpers.iter_entry_points') as iep:
+            with mock.patch.dict(tg.config, order):
+                iep.return_value = eps
+                sections = app.profile_sections
+                assert_equal(sections, [
+                        eps[1].load(),
+                        eps[3].load(),
+                        eps[2].load(),
+                        eps[0].load(),
+                    ])
+                r = self.app.get('/u/test-user/profile')
+                assert_in('Section a', r.body)
+                assert_in('Section b', r.body)
+                assert_in('Section c', r.body)
+                assert_in('Section d', r.body)
+                assert_not_in('Section f', r.body)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/ForgeActivity/forgeactivity/main.py
----------------------------------------------------------------------
diff --git a/ForgeActivity/forgeactivity/main.py b/ForgeActivity/forgeactivity/main.py
index 6bf1199..6d50427 100644
--- a/ForgeActivity/forgeactivity/main.py
+++ b/ForgeActivity/forgeactivity/main.py
@@ -33,6 +33,8 @@ from allura.lib.security import require_authenticated
 from allura.model.timeline import perm_check
 from allura.lib import helpers as h
 from allura.lib.decorators import require_post
+from allura.model.timeline import perm_check
+from allura.ext.user_profile import ProfileSectionBase
 
 from .widgets.follow import FollowToggle
 
@@ -199,3 +201,29 @@ class ForgeActivityRestController(BaseController):
                 'target': a.target._deinstrument(),
             } for a in data['timeline']],
         }
+
+
+class ForgeActivityProfileSection(ProfileSectionBase):
+    template = 'forgeactivity:templates/widgets/profile_section.html'
+
+    def __init__(self, user, project):
+        self.user = user
+        self.project = project
+        self.activity_app = self.project.app_instance('activity')
+
+    def check_display(self):
+        return self.activity_app is not None
+
+    def prepare_context(self, context):
+        context.update({
+                'user': self.user,
+                'follow_toggle': W.follow_toggle,
+                'following': g.director.is_connected(c.user, self.user),
+                'timeline': g.director.get_timeline(
+                    self.user, page=0, limit=5,
+                    actor_only=True,
+                    filter_func=perm_check(c.user)),
+                'activity_app': self.activity_app,
+            })
+        g.register_js('activity_js/follow.js')
+        return context

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/ForgeActivity/forgeactivity/templates/index.html
----------------------------------------------------------------------
diff --git a/ForgeActivity/forgeactivity/templates/index.html b/ForgeActivity/forgeactivity/templates/index.html
index 8293763..435fc82 100644
--- a/ForgeActivity/forgeactivity/templates/index.html
+++ b/ForgeActivity/forgeactivity/templates/index.html
@@ -18,6 +18,7 @@
 -#}
 {% set hide_left_bar = True %}
 {% extends g.theme.master %}
+{% import 'forgeactivity:templates/macros.html' as am with context %}
 
 {% block title %}{{c.project.name}} Activity{% endblock %}
 
@@ -36,21 +37,6 @@
     {% endif %}
 {% endblock %}
 
-{% macro activity_obj(o) %}
-  <a href="{{o.activity_url}}">{{o.activity_name}}</a>
-{% endmacro %}
-
-{% macro icon(o, size, className) -%}
-  {% if o.activity_extras.get('icon_url') %}
-    <img src="{{ o.activity_extras.get('icon_url') }}"
-         alt="{{ o.activity_name }}"
-         title="{{ o.activity_name }}"
-         class="emboss{% if size %} x{{size}}{% endif %}{% if className %} {{className}}{%
endif %}">
-  {% else %}
-    <b data-icon="{{g.icons['user'].char}}" class="ico emboss {{g.icons['user'].css}}{%
if size %} x{{size}}{% endif %}{% if className %} {{className}}{% endif %}"></b>
-  {% endif %}
-{%- endmacro %}
-
 {% block content %}
 <div class="activity">
   {% if not timeline %}
@@ -61,8 +47,8 @@
         <li>
           <time datetime="{{a.published|datetimeformat}}" title="{{a.published|datetimeformat}}">{{h.ago(a.published,
show_date_after=None)}}</time>
           <h1>
-              {{ icon(a.actor, 32, 'avatar') }}
-              {{activity_obj(a.actor)}} {{a.verb}} {{activity_obj(a.obj)}} {% if a.target.activity_name
%}on {{activity_obj(a.target)}}{% endif %}
+              {{ am.icon(a.actor, 32, 'avatar') }}
+              {{am.activity_obj(a.actor)}} {{a.verb}} {{am.activity_obj(a.obj)}} {% if a.target.activity_name
%}on {{am.activity_obj(a.target)}}{% endif %}
           </h1>
           {% if a.obj.activity_extras.get('summary') %}
           <p>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/ForgeActivity/forgeactivity/templates/macros.html
----------------------------------------------------------------------
diff --git a/ForgeActivity/forgeactivity/templates/macros.html b/ForgeActivity/forgeactivity/templates/macros.html
new file mode 100644
index 0000000..c60582e
--- /dev/null
+++ b/ForgeActivity/forgeactivity/templates/macros.html
@@ -0,0 +1,33 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+
+{% macro activity_obj(o) %}
+  <a href="{{o.activity_url}}">{{o.activity_name}}</a>
+{% endmacro %}
+
+{% macro icon(o, size, className) -%}
+  {% if o.activity_extras.get('icon_url') %}
+    <img src="{{ o.activity_extras.get('icon_url') }}"
+         alt="{{ o.activity_name }}"
+         title="{{ o.activity_name }}"
+         class="emboss{% if size %} x{{size}}{% endif %}{% if className %} {{className}}{%
endif %}">
+  {% else %}
+    <b data-icon="{{g.icons['user'].char}}" class="ico emboss {{g.icons['user'].css}}{%
if size %} x{{size}}{% endif %}{% if className %} {{className}}{% endif %}"></b>
+  {% endif %}
+{%- endmacro %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
----------------------------------------------------------------------
diff --git a/ForgeActivity/forgeactivity/templates/widgets/profile_section.html b/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
new file mode 100644
index 0000000..a9e473f
--- /dev/null
+++ b/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
@@ -0,0 +1,51 @@
+{#-
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
+
+       Unless required by applicable law or agreed to in writing,
+       software distributed under the License is distributed on an
+       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+       KIND, either express or implied.  See the License for the
+       specific language governing permissions and limitations
+       under the License.
+-#}
+{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
+{% import 'forgeactivity:templates/macros.html' as am with context %}
+
+<div class="user-activity grid-8">
+    <h3>
+        User Activity
+
+        {% if c.user and not c.user.is_anonymous() and c.user != user %}
+            {{follow_toggle.display(following=following, action=activity_app.url+'follow')}}
+        {% endif %}
+    </h3>
+    {% if not timeline %}
+        <p class="empty">No activity to display.</p>
+    {% else %}
+    <ul class="timeline">
+        {% for a in timeline %}
+        <li>
+            <b>
+                {{am.icon(a.actor, 16, 'avatar')}}{{am.activity_obj(a.actor)}} {{a.verb}}
{{am.activity_obj(a.obj)}}
+                {% if a.target.activity_name %}on {{am.activity_obj(a.target)}}{% endif %}
+            </b>
+            {% if a.obj.activity_extras.get('summary') %}
+            <p>
+                {{ a.obj.activity_extras.get('summary') }}
+            </p>
+            {% endif %}
+            <time datetime="{{a.published|datetimeformat}}" title="{{a.published|datetimeformat}}">{{h.ago(a.published,
show_date_after=None)}}</time>
+        </li>
+        {% endfor %}
+    </ul>
+    <a class="view-all" href="{{activity_app.url}}">View All</a>
+    {% endif %}
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cae66cc4/ForgeActivity/setup.py
----------------------------------------------------------------------
diff --git a/ForgeActivity/setup.py b/ForgeActivity/setup.py
index 45c546e..9795dfb 100644
--- a/ForgeActivity/setup.py
+++ b/ForgeActivity/setup.py
@@ -41,6 +41,9 @@ setup(name='ForgeActivity',
       [allura]
       activity=forgeactivity.main:ForgeActivityApp
 
+      [allura.user_profile.sections]
+      activity=forgeactivity.main:ForgeActivityProfileSection
+
       [easy_widgets.resources]
       ew_resources=forgeactivity.config.resources:register_ew_resources
       """,


Mime
View raw message