[#8438] dynamically use current avatar urls in activity stream; fix Anonymous linking
diff --git a/ForgeActivity/forgeactivity/main.py b/ForgeActivity/forgeactivity/main.py
index e8b5333..9b63993 100644
--- a/ForgeActivity/forgeactivity/main.py
+++ b/ForgeActivity/forgeactivity/main.py
@@ -124,15 +124,31 @@
 
         filtered_timeline = list(islice(filter(perm_check(c.user), timeline),
                                         0, limit))
-        if config.get("default_avatar_image"):
-            for t in filtered_timeline:
-                if not t.actor.activity_extras.get('icon_url'):
-                    t.actor.activity_extras.icon_url = config['default_avatar_image']
+        use_gravatar = h.asbool(config.get('use_gravatar'))
+        default_avatar = config.get("default_avatar_image")
+        icon_base = config.get('static.icon_base', '')  # CDN, possibly
+        for t in filtered_timeline:
+            # fix broken links for Anonymous user
+            if t.actor.activity_url == '/u/userid-None/':
+                t.actor.activity_url = None
+            # fix avatars
+            if not use_gravatar:
+                # force current user icon (overwrites previous gravatar urls or defaults)
+                if t.actor.activity_url:
+                    t.actor.activity_extras.icon_url = icon_base + t.actor.activity_url + 'user_icon'
+                    # ideally ?{icon_timestamp} would be appended to URL for cache-busting when CDN is used, but that
+                    # value would only be available by querying and loading the user-project
                 else:
+                    t.actor.activity_extras.icon_url = None
+            elif default_avatar:
+                if not t.actor.activity_extras.get('icon_url'):
+                    t.actor.activity_extras.icon_url = default_avatar
+                else:
+                    # fix gravatar urls with old default avatar urls in them
                     t.actor.activity_extras.icon_url = re.sub(r'([&?])d=[^&]*',
-                                                              r'\1d={}'.format(config["default_avatar_image"]),
+                                                              r'\1d={}'.format(default_avatar),
                                                               t.actor.activity_extras.icon_url)
-                session(t).expunge(t)  # don't save back this change
+            session(t).expunge(t)  # don't save back these changes
 
         if extra_limit == limit:
             # if we didn't ask for extra, then we expect there's more if we got all we asked for
diff --git a/ForgeActivity/forgeactivity/tests/functional/test_root.py b/ForgeActivity/forgeactivity/tests/functional/test_root.py
index 6a58e3e..cdbf679 100644
--- a/ForgeActivity/forgeactivity/tests/functional/test_root.py
+++ b/ForgeActivity/forgeactivity/tests/functional/test_root.py
@@ -61,10 +61,7 @@
     @td.with_tool('test', 'activity')
     @patch('forgeactivity.main.g.director')
     def test_index_html(self, director):
-        from activitystream.storage.base import StoredActivity
-        from bson import ObjectId
-        from datadiff.tools import assert_equal
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "_id": ObjectId("529fa331033c5e6406d8b338"),
             "obj": {
                 "activity_extras": {
@@ -182,9 +179,7 @@
     @td.with_tool('test', 'activity')
     @patch('forgeactivity.main.g.director')
     def test_feed_rss_project(self, director):
-        from activitystream.storage.base import StoredActivity
-        from bson import ObjectId
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "_id": ObjectId("529fa331033c5e6406d8b338"),
             "obj": {
                 "activity_extras": {
@@ -234,8 +229,7 @@
     @td.with_tool('test', 'activity')
     @patch('forgeactivity.main.g.director')
     def test_feed_rss_project_verb_without_activity_name(self, director):
-        from activitystream.storage.base import StoredActivity
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "obj": {
                 "activity_extras": {},
                 "activity_url": "/p/test/tickets/34/?limit=25#ed7c",
@@ -259,9 +253,7 @@
     @td.with_user_project('test-user-1')
     @patch('forgeactivity.main.g.director')
     def test_feed_rss_user(self, director):
-        from activitystream.storage.base import StoredActivity
-        from bson import ObjectId
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "_id": ObjectId("529fa331033c5e6406d8b338"),
             "obj": {
                 "activity_extras": {
@@ -309,9 +301,7 @@
     @td.with_tool('test', 'activity')
     @patch('forgeactivity.main.g.director')
     def test_feed_atom_project(self, director):
-        from activitystream.storage.base import StoredActivity
-        from bson import ObjectId
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "_id": ObjectId("529fa331033c5e6406d8b338"),
             "obj": {
                 "activity_extras": {
@@ -365,9 +355,7 @@
     @td.with_user_project('test-user-1')
     @patch('forgeactivity.main.g.director')
     def test_feed_atom_user(self, director):
-        from activitystream.storage.base import StoredActivity
-        from bson import ObjectId
-        director.get_timeline.return_value = [StoredActivity(**{
+        director.get_timeline.return_value = [Activity(**{
             "_id": ObjectId("529fa331033c5e6406d8b338"),
             "obj": {
                 "activity_extras": {