[#7205] Clean up timeline on user profile page
Signed-off-by: Tim Van Steenburgh <tvansteenburgh@gmail.com>
diff --git a/Allura/allura/model/timeline.py b/Allura/allura/model/timeline.py
index 7530221..e9d9d99 100644
--- a/Allura/allura/model/timeline.py
+++ b/Allura/allura/model/timeline.py
@@ -115,24 +115,32 @@
self.activity_name = activity_name
+def get_activity_object(activity_object_dict):
+ """Given a BSON-serialized activity object (e.g. activity.obj dict in a
+ timeline), return the corresponding :class:`ActivityObject`.
+
+ """
+ extras_dict = activity_object_dict.activity_extras
+ if not extras_dict:
+ return None
+ allura_id = extras_dict.get('allura_id')
+ if not allura_id:
+ return None
+ classname, _id = allura_id.split(':', 1)
+ cls = Mapper.by_classname(classname).mapped_class
+ try:
+ _id = bson.ObjectId(_id)
+ except bson.errors.InvalidId:
+ pass
+ return cls.query.get(_id=_id)
+
+
def perm_check(user):
"""
Return a function that returns True if ``user`` has 'read' access to a given activity,
otherwise returns False.
"""
def _perm_check(activity):
- extras_dict = activity.obj.activity_extras
- if not extras_dict:
- return True
- allura_id = extras_dict.get('allura_id')
- if not allura_id:
- return True
- classname, _id = allura_id.split(':', 1)
- cls = Mapper.by_classname(classname).mapped_class
- try:
- _id = bson.ObjectId(_id)
- except bson.errors.InvalidId:
- pass
- obj = cls.query.get(_id=_id)
- return obj and obj.has_activity_access('read', user, activity)
+ obj = get_activity_object(activity.obj)
+ return obj is None or obj.has_activity_access('read', user, activity)
return _perm_check
diff --git a/ForgeActivity/forgeactivity/main.py b/ForgeActivity/forgeactivity/main.py
index ca6ceaf..6af2da8 100644
--- a/ForgeActivity/forgeactivity/main.py
+++ b/ForgeActivity/forgeactivity/main.py
@@ -19,6 +19,7 @@
import calendar
from itertools import islice, ifilter
+from ming.orm import session
from pylons import tmpl_context as c, app_globals as g
from pylons import request, response
from tg import expose, validate, config
@@ -32,7 +33,7 @@
from allura import model as M
from allura.controllers import BaseController
from allura.lib.security import require_authenticated, require_access
-from allura.model.timeline import perm_check
+from allura.model.timeline import perm_check, get_activity_object
from allura.lib import helpers as h
from allura.lib.decorators import require_post
from allura.lib.widgets.form_fields import PageList
@@ -254,6 +255,18 @@
)
filtered_timeline = list(islice(ifilter(perm_check(c.user), full_timeline),
0, 8))
+ for activity in filtered_timeline:
+ # Get the project for the activity.obj so we can use it in the
+ # template. Expunge first so Ming doesn't try to flush the attr
+ # we create to temporarily store the project.
+ #
+ # The get_activity_object() calls are cheap, pulling from
+ # the session identity map instead of mongo since identical
+ # calls are made by perm_check() above.
+ session(activity).expunge(activity)
+ activity_obj = get_activity_object(activity.obj)
+ activity.obj.project = activity_obj.project if activity_obj else None
+
context.update({
'follow_toggle': W.follow_toggle,
'following': g.director.is_connected(c.user, self.user),
diff --git a/ForgeActivity/forgeactivity/templates/widgets/profile_section.html b/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
index 28ba645..8d09ef3 100644
--- a/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
+++ b/ForgeActivity/forgeactivity/templates/widgets/profile_section.html
@@ -41,8 +41,9 @@
{% for a in timeline %}
<li>
<b>
- {{am.icon(a.actor, 16, 'avatar')}}{{am.activity_obj(a.actor)}} {{a.verb}} {{am.activity_obj(a.obj)}}
+ {{a.verb.capitalize()}} {{am.activity_obj(a.obj)}}
{% if a.target.activity_name %}on {{am.activity_obj(a.target)}}{% endif %}
+ {% if a.obj.project %}on <a href="{{a.obj.project.url()}}">{{a.obj.project.name}}</a>{% endif %}
</b>
{% if a.obj.activity_extras.get('summary') %}
<p>