[#6714] Refactored User.project_role into (unused) ProjectRole.by_user

Signed-off-by: Cory Johns <cjohns@slashdotmedia.com>
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 150816e..259400f 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -611,7 +611,7 @@
         user = model.User.by_username(username)
         if not user:
             return dict(error='User "%s" not found' % username)
-        ace = model.ACE.deny(user.project_role()._id, perm, reason)
+        ace = model.ACE.deny(model.ProjectRole.by_user(user, upsert=True)._id, perm, reason)
         if not model.ACL.contains(ace, self.app.acl):
             self.app.acl.append(ace)
             return dict(user_id=str(user._id), username=user.username, reason=reason)
@@ -631,7 +631,7 @@
             return dict(error='Select user to unblock')
         unblocked = []
         for user in users:
-            ace = model.ACE.deny(user.project_role()._id, perm)
+            ace = model.ACE.deny(model.ProjectRole.by_user(user)._id, perm)
             ace = model.ACL.contains(ace, self.app.acl)
             if ace:
                 self.app.acl.remove(ace)
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index e36e52e..0dbb3c3 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -341,7 +341,7 @@
         admins = []
         developers = []
         for user in c.project.users():
-            roles = M.ProjectRole.query.find({'_id': {'$in': user.project_role().roles}})
+            roles = M.ProjectRole.query.find({'_id': {'$in': M.ProjectRole.by_user(user).roles}})
             roles = set([r.name for r in roles])
             u = dict(
                     display_name=user.display_name,
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 314c3a9..0649329 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -821,10 +821,11 @@
             return dict(error='Could not find group with id %s' % role_id)
         if not user:
             return dict(error='User %s not found' % username)
-        if group._id in user.project_role().roles:
+        user_role = M.ProjectRole.by_user(user, upsert=True)
+        if group._id in user_role.roles:
             return dict(error='%s (%s) is already in the group %s.' % (user.display_name, username, group.name))
         M.AuditLog.log('add user %s to %s', username, group.name)
-        user.project_role().roles.append(group._id)
+        user_role.roles.append(group._id)
         if group.name == 'Admin':
             for ac in c.project.app_configs:
                 c.project.app_instance(ac).subscribe(user)
@@ -844,10 +845,11 @@
             return dict(error='Could not find group with id %s' % role_id)
         if not user:
             return dict(error='User %s not found' % username)
-        if group._id not in user.project_role().roles:
+        user_role = M.ProjectRole.by_user(user)
+        if not user_role or group._id not in user_role.roles:
             return dict(error='%s (%s) is not in the group %s.' % (user.display_name, username, group.name))
         M.AuditLog.log('remove user %s from %s', username, group.name)
-        user.project_role().roles.remove(group._id)
+        user_role.roles.remove(group._id)
         g.post_event('project_updated')
         return dict()
 
@@ -875,7 +877,7 @@
                 if not user._id:
                     continue # never add anon users to groups
                 M.AuditLog.log('add user %s to %s', username, group.name)
-                user.project_role().roles.append(group._id)
+                M.ProjectRole.by_user(user, upsert=True).roles.append(group._id)
                 user_added = True
             # Make sure we aren't removing all users from the Admin group
             if group.name == u'Admin' and not (user_ids or user_added):
diff --git a/Allura/allura/ext/project_home/project_main.py b/Allura/allura/ext/project_home/project_main.py
index 3df3d37..e2e9b94 100644
--- a/Allura/allura/ext/project_home/project_main.py
+++ b/Allura/allura/ext/project_home/project_main.py
@@ -77,7 +77,7 @@
 
     def install(self, project):
         super(ProjectHomeApp, self).install(project)
-        pr = c.user.project_role()
+        pr = model.ProjectRole.by_user(c.user)
         if pr:
             self.config.acl = [
                 model.ACE.allow(pr._id, perm)
diff --git a/Allura/allura/ext/user_profile/user_main.py b/Allura/allura/ext/user_profile/user_main.py
index 6debee8..09e436c 100644
--- a/Allura/allura/ext/user_profile/user_main.py
+++ b/Allura/allura/ext/user_profile/user_main.py
@@ -31,7 +31,7 @@
 from allura.lib.helpers import DateTimeConverter
 from allura.lib.security import require_access
 from allura.lib.plugin import AuthenticationProvider
-from allura.model import User, Feed, ACE
+from allura.model import User, Feed, ACE, ProjectRole
 from allura.controllers import BaseController
 from allura.controllers.feed import FeedArgs, FeedController
 from allura.lib.decorators import require_post
@@ -71,7 +71,7 @@
         return c.project.is_root
 
     def install(self, project):
-        pr = c.user.project_role()
+        pr = ProjectRole.by_user(c.user)
         if pr:
             self.config.acl = [
                 ACE.allow(pr._id, perm)
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index c09548e..a547577 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -536,7 +536,7 @@
                 for username in usernames:
                     guser = M.User.by_username(username)
                     if not (guser and guser._id): continue
-                    pr = guser.project_role(project=p)
+                    pr = M.ProjectRole.by_user(guser, project=p, upsert=True)
                     if group._id not in pr.roles:
                         pr.roles.append(group._id)
         if 'tools' in project_template:
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index d8c0f1c..18e8b2e 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -671,13 +671,6 @@
             seen_project_ids.add(r.project_id)
             yield r.project
 
-    def project_role(self, project=None):
-        if project is None: project = c.project
-        if self.is_anonymous():
-            return ProjectRole.anonymous(project)
-        else:
-            return ProjectRole.upsert(user_id=self._id, project_id=project.root_project._id)
-
     def set_password(self, new_password):
         return plugin.AuthenticationProvider.get(request).set_password(
             self, self.password, new_password)
@@ -760,19 +753,20 @@
         return '**unknown name role: %s' % self._id # pragma no cover
 
     @classmethod
-    def by_user(cls, user=None, project=None):
-        if user is None and project is None:
-            return c.user.current_project_role
-        if user is None: user = c.user
+    def by_user(cls, user, project=None, upsert=False):
         if project is None: project = c.project
-        pr = cls.query.get(
-            user_id=user._id,
-            project_id=project.root_project._id)
-        if pr is None:
-            pr = cls.query.get(
-                user_id=user._id,
-                project_id={'$exists':False})
-        return pr
+        if user.is_anonymous():
+            return cls.anonymous(project)
+        if upsert:
+            return cls.upsert(
+                    user_id=user._id,
+                    project_id=project.root_project._id,
+                )
+        else:
+            return cls.query.get(
+                    user_id=user._id,
+                    project_id=project.root_project._id,
+                )
 
     @classmethod
     def by_name(cls, name, project=None):
diff --git a/Allura/allura/model/discuss.py b/Allura/allura/model/discuss.py
index 394ad2e..a021c12 100644
--- a/Allura/allura/model/discuss.py
+++ b/Allura/allura/model/discuss.py
@@ -35,7 +35,7 @@
 from allura.model.notification import Notification, Mailbox
 from .artifact import Artifact, ArtifactReference, VersionedArtifact, Snapshot, Message, Feed
 from .attachments import BaseAttachment
-from .auth import User
+from .auth import User, ProjectRole
 from .timeline import ActivityObject
 from .types import MarkdownCache
 
@@ -626,13 +626,14 @@
             return
         self.status = 'ok'
         author = self.author()
+        author_role = ProjectRole.by_user(author, project=self.project, upsert=True)
         security.simple_grant(
-            self.acl, author.project_role(self.project)._id, 'moderate')
+            self.acl, author_role._id, 'moderate')
         self.commit()
         if (c.app.config.options.get('PostingPolicy') == 'ApproveOnceModerated'
             and author._id != None):
             security.simple_grant(
-                self.acl, author.project_role()._id, 'unmoderated_post')
+                self.acl, author_role._id, 'unmoderated_post')
         if notify:
             self.notify(file_info=file_info)
         artifact = self.thread.artifact or self.thread
diff --git a/Allura/allura/model/project.py b/Allura/allura/model/project.py
index ff8c0f4..e1cae14 100644
--- a/Allura/allura/model/project.py
+++ b/Allura/allura/model/project.py
@@ -789,7 +789,7 @@
                 for perm in self.permissions ]
             self.private = is_private_project
             for user in users:
-                pr = user.project_role()
+                pr = ProjectRole.by_user(user, project=self, upsert=True)
                 pr.roles = [ role_admin._id ]
             session(self).flush(self)
             # Setup apps
@@ -801,7 +801,7 @@
 
     def add_user(self, user, role_names):
         'Convenience method to add member with the given role(s).'
-        pr = user.project_role(self)
+        pr = ProjectRole.by_user(user, project=self, upsert=True)
         for role_name in role_names:
             r = ProjectRole.by_name(role_name, self)
             pr.roles.append(r._id)
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 66cffba..d325351 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -185,7 +185,7 @@
         r = self.app.post('/admin/wiki/block_user', params={'username': 'test-admin', 'perm': 'read', 'reason': 'Comment'})
         assert_equals(r.json, dict(user_id=str(user._id), username='test-admin', reason='Comment'))
         user = M.User.by_username('test-admin')
-        admin_role = user.project_role()
+        admin_role = M.ProjectRole.by_user(user)
         app = M.Project.query.get(shortname='test').app_instance('wiki')
         ace = M.ACL.contains(M.ACE.deny(admin_role._id, 'read'), app.acl)
         assert_equals(ace.reason, 'Comment')
@@ -196,7 +196,7 @@
     def test_unblock_user(self):
         r = self.app.post('/admin/wiki/block_user', params={'username': 'test-admin', 'perm': 'read'})
         user = M.User.by_username('test-admin')
-        admin_role = user.project_role()
+        admin_role = M.ProjectRole.by_user(user)
         app = M.Project.query.get(shortname='test').app_instance('wiki')
         ace = M.ACE.deny(admin_role._id, 'read')
         r = self.app.get('/admin/wiki/permissions')
@@ -215,8 +215,8 @@
         self.app.post('/admin/wiki/block_user', params={'username': 'test-user', 'perm': 'read'})
         admin = M.User.by_username('test-admin')
         user = M.User.by_username('test-user')
-        admin_role = admin.project_role()
-        user_role = user.project_role()
+        admin_role = M.ProjectRole.by_user(admin)
+        user_role = M.ProjectRole.by_user(user)
         app = M.Project.query.get(shortname='test').app_instance('wiki')
         deny_admin = M.ACE.deny(admin_role._id, 'read')
         deny_user = M.ACE.deny(user_role._id, 'read')
@@ -239,7 +239,7 @@
         self.app.post('/admin/wiki/block_user', params={'username': 'test-user', 'perm': 'read', 'reason': 'Comment'})
         self.app.post('/admin/wiki/block_user', params={'username': 'test-user', 'perm': 'post', 'reason': 'Comment'})
         user = M.User.by_username('test-user')
-        user_role = user.project_role()
+        user_role = M.ProjectRole.by_user(user)
         app = M.Project.query.get(shortname='test').app_instance('wiki')
         assert M.ACL.contains(M.ACE.deny(user_role._id, 'read'), app.acl)
         assert M.ACL.contains(M.ACE.deny(user_role._id, 'post'), app.acl)
diff --git a/Allura/allura/tests/functional/test_neighborhood.py b/Allura/allura/tests/functional/test_neighborhood.py
index f63d597..c9dc1fe 100644
--- a/Allura/allura/tests/functional/test_neighborhood.py
+++ b/Allura/allura/tests/functional/test_neighborhood.py
@@ -689,7 +689,7 @@
                 for username in usernames:
                     user = M.User.by_username(username)
                     if user and user._id:
-                        assert role in user.project_role(project=p).roles
+                        assert role in M.ProjectRole.by_user(user, project=p).roles
             # confirm roles with invalid json data are not created
             if name in ('', 'TestGroup1'):
                 assert name not in roles
diff --git a/Allura/allura/tests/model/test_artifact.py b/Allura/allura/tests/model/test_artifact.py
index cc27a7b..67c0d8b 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -78,7 +78,7 @@
     assert pg.app.config == c.app.config
     assert pg.app_config == c.app.config
     u = M.User.query.get(username='test-user')
-    pr = u.project_role()
+    pr = M.ProjectRole.by_user(u, upsert=True)
     ThreadLocalORMSession.flush_all()
     REGISTRY.register(allura.credentials, allura.lib.security.Credentials())
     assert not security.has_access(pg, 'delete')(user=u)
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 433a6be..29acb6a 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -86,7 +86,6 @@
     p = M.Project.query.get(shortname='test2')
     p.deleted = True
     assert_equal(set(p.shortname for p in c.user.my_projects()), set(['test', 'u/test-admin', 'adobe-1', '--init--']))
-    assert_equal(M.User.anonymous().project_role().name, '*anonymous')
     u = M.User.register(dict(
             username='nosetest-user'))
     ThreadLocalORMSession.flush_all()
@@ -148,7 +147,7 @@
 @with_setup(setUp)
 def test_project_role():
     role = M.ProjectRole(project_id=c.project._id, name='test_role')
-    c.user.project_role().roles.append(role._id)
+    M.ProjectRole.by_user(c.user, upsert=True).roles.append(role._id)
     ThreadLocalORMSession.flush_all()
     roles = g.credentials.user_roles(
         c.user._id, project_id=c.project.root_project._id)
@@ -217,7 +216,7 @@
     project = M.Project.query.get(shortname='test2')
     admin_role = M.ProjectRole.by_name('Admin', project)
     developer_role = M.ProjectRole.by_name('Developer', project)
-    user_role = c.user.project_role(project)
+    user_role = M.ProjectRole.by_user(c.user, project=project, upsert=True)
     user_role.roles.remove(admin_role._id)
     user_role.roles.append(developer_role._id)
     ThreadLocalORMSession.flush_all()
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index c11c8d3..94f3ced 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -168,7 +168,7 @@
         # Make project private & verify we don't see its new feed items
         anon = M.User.anonymous()
         p_test.acl.insert(0, M.ACE.deny(
-                anon.project_role(p_test)._id, 'read'))
+                M.ProjectRole.anonymous(p_test)._id, 'read'))
         ThreadLocalORMSession.flush_all()
         pg = WM.Page.query.get(title='Home', app_config_id=c.app.config._id)
         pg.text = 'Change'
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 38a67c5..bdd3cea 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -104,8 +104,7 @@
 
 def test_make_roles():
     h.set_context('test', 'wiki', neighborhood='Projects')
-    u = M.User.anonymous()
-    pr = u.project_role()
+    pr = M.ProjectRole.anonymous()
     assert h.make_roles([pr._id]).next() == pr
 
 @td.with_wiki
diff --git a/Allura/allura/tests/test_security.py b/Allura/allura/tests/test_security.py
index cbc8df0..0acc5ca 100644
--- a/Allura/allura/tests/test_security.py
+++ b/Allura/allura/tests/test_security.py
@@ -38,7 +38,7 @@
     Credentials.get().clear()
 
 def _add_to_group(user, role):
-    user.project_role().roles.append(role._id)
+    M.ProjectRole.by_user(user, upsert=True).roles.append(role._id)
     ThreadLocalODMSession.flush_all()
     Credentials.get().clear()
 
@@ -169,6 +169,6 @@
         wiki = c.project.app_instance('wiki')
         user = M.User.by_username('test-user')
         assert has_access(wiki, 'read', user)()
-        wiki.acl.append(M.ACE.deny(user.project_role()._id, 'read', 'Spammer'))
+        wiki.acl.append(M.ACE.deny(M.ProjectRole.by_user(user, upsert=True)._id, 'read', 'Spammer'))
         Credentials.get().clear()
         assert not has_access(wiki, 'read', user)()
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index c113504..50ec351 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -755,7 +755,7 @@
     def _set_private(self, bool_flag):
         if bool_flag:
             role_developer = ProjectRole.by_name('Developer')
-            role_creator = self.reported_by.project_role()
+            role_creator = ProjectRole.by_user(self.reported_by, upsert=True)
             _allow_all = lambda role, perms: [ACE.allow(role._id, perm) for perm in perms]
             # maintain existing access for developers and the ticket creator,
             # but revoke all access for everyone else
diff --git a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
index 90eba99..ca532ba 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
@@ -95,8 +95,8 @@
         assert creator == t.reported_by
         role_admin = ProjectRole.by_name('Admin')._id
         role_developer = ProjectRole.by_name('Developer')._id
-        role_creator = t.reported_by.project_role()._id
-        developer.project_role().roles.append(role_developer)
+        role_creator = ProjectRole.by_user(t.reported_by, upsert=True)._id
+        ProjectRole.by_user(developer, upsert=True).roles.append(role_developer)
         ThreadLocalORMSession.flush_all()
         cred = Credentials.get().clear()
 
diff --git a/scripts/add_user_to_group.py b/scripts/add_user_to_group.py
index f7b3fad..1047999 100644
--- a/scripts/add_user_to_group.py
+++ b/scripts/add_user_to_group.py
@@ -53,7 +53,7 @@
     project_role = M.ProjectRole.by_name(options.group, project=project)
     if not project_role:
         return "Couldn't find group (ProjectRole) with name '%s'" % options.group
-    user_roles = user.project_role(project=project).roles
+    user_roles = M.ProjectRole.by_user(user, project=project, upsert=True).roles
     if project_role._id not in user_roles:
         user_roles.append(project_role._id)
     ThreadLocalORMSession.flush_all()
diff --git a/scripts/teamforge-import.py b/scripts/teamforge-import.py
index bd8b2d2..f86b063 100644
--- a/scripts/teamforge-import.py
+++ b/scripts/teamforge-import.py
@@ -334,7 +334,7 @@
         admin_usernames.add(admin.userName)
         user = get_user(admin.userName)
         c.user = user
-        pr = user.project_role(project)
+        pr = M.ProjectRole.by_user(user, project=project, upsert=True)
         pr.roles = [ role_admin._id ]
         ThreadLocalORMSession.flush_all()
     role_developer = M.ProjectRole.by_name('Developer', project)
@@ -345,7 +345,7 @@
         if member.userName in admin_usernames:
             continue
         user = get_user(member.userName)
-        pr = user.project_role(project)
+        pr = M.ProjectRole.by_user(user, project=project, upsert=True)
         pr.roles = [ role_developer._id ]
         ThreadLocalORMSession.flush_all()
     project.labels = [cat.path.split('projects/categorization.root.')[1] for cat in data.categories]