{"action":"created","comment":{"url":"https://api.github.com/repos/FUUK/fuuk/pulls/comments/78569353","id":78569353,"diff_hunk":"@@ -147,39 +149,118 @@ class AgencyAdmin(TranslationAdmin):\n     search_fields = get_translation_fields('name') + get_translation_fields('shortcut')\n \n \n+class GrantCollaboratorInlineAdmin(TabularInline):\n+    \"\"\"\n+    `GrantCollaborator` objects can be added only through GrantAdmin. Each user who can change the grant can modifie\n+    attached GrantCollaborators too. `GrantCollaboratorInlineFormSet` checks if `Grant.investigator_human` is not among\n+    `GrantCollaborator.human` objects.\n+    \"\"\"\n+    formset = GrantCollaboratorInlineFormSet\n+    model = GrantCollaborator\n+    extra = 3\n+\n+    def has_add_permission(self, request):\n+        return True\n+\n+    def has_change_permission(self, request, obj=None):\n+        return True\n+\n+    def has_delete_permission(self, request, obj=None):\n+        return True\n+\n+\n class GrantAdmin(TranslationAdmin):\n-    list_display = ('author', 'number', 'title', 'start', 'end')\n+    list_display = ('investigator_name', 'number', 'title', 'start', 'end')\n     list_filter = ('agency', 'start')\n     ordering = ('-end', )\n     search_fields = ['number'] + get_translation_fields('title')\n-    filter_horizontal = ('co_authors',)\n+    inlines = (GrantCollaboratorInlineAdmin, )\n+\n+    def get_readonly_fields(self, request, obj=None):\n+        \"\"\"\n+        `editor` can be changed only by the user with the change permission for grants.\n+        \"\"\"\n+        if super(GrantAdmin, self).has_change_permission(request, obj):\n+            return super(GrantAdmin, self).get_readonly_fields(request, obj)\n+\n+        fields = list(super(GrantAdmin, self).get_readonly_fields(request, obj))\n+        fields.extend(['editor', ])\n+        return tuple(fields)\n+\n+    def has_module_permission(self, request):\n+        \"\"\"\n+        Everybody can add a `grant`, so everybody should see the grant application in the index admin page.\n+        \"\"\"\n+        return True\n+\n+    def has_add_permission(self, request):\n+        \"\"\"\n+        Everybody can add a `grant`.\n+        \"\"\"\n+        return True\n \n     def has_change_permission(self, request, obj=None):\n         \"\"\"\n-        If `obj` is None, this should return True if the given request has\n-        permission to delete *any* object of the given type.\n+        The change permission has user, which is the editor, author or collaborator or has change permission for grants.\n+        If `obj` is None, this should return True if the given request has permission to change *any* grant.\n         \"\"\"\n-        if request.user.is_superuser:\n-            return True\n+        if obj:\n+            if request.user == obj.editor or \\\n+                    request.user == getattr(obj.investigator_human, 'user', None):\n+                return True\n+            if obj.collaborators.filter(human__user=request.user).exists():\n+                return True\n+            return super(GrantAdmin, self).has_change_permission(request, obj)\n+\n+        return True\n \n+    def has_delete_permission(self, request, obj=None):\n+        \"\"\"\n+        The delete permission has user, which is the editor, author or collaborator or has delete permission for grants.\n+        If `obj` is None, this should return True if the given request has permission to delete *any* grant.\n+        \"\"\"\n         if obj:\n-            if request.user == getattr(obj.author.human, 'user', None):\n+            if request.user == obj.editor or \\\n+                    request.user == getattr(obj.investigator_human, 'user', None):\n                 return True\n-            for author in obj.co_authors.all():\n-                if request.user == getattr(author.human, 'user', None):\n-                    return True\n-            return False\n+            if obj.collaborators.filter(human__user=request.user).exists():\n+                return True\n+            return super(GrantAdmin, self).has_delete_permission(request, obj)\n \n-        return super(GrantAdmin, self).has_change_permission(request, obj)\n+        return True\n \n     def get_queryset(self, request):\n+        \"\"\"\n+        User with change permission can view all grants, other users can view only grants, where they are editor,\n+        author or collaborator.\n+        \"\"\"\n         queryset = super(GrantAdmin, self).get_queryset(request)\n-        if request.user.is_superuser:\n+        opts = self.opts\n+        codename = get_permission_codename('change', opts)\n+        if request.user.has_perm(\"%s.%s\" % (opts.app_label, codename)):\n             return queryset\n-        human = request.user.human\n-        return queryset.filter(\n-            pk__in=queryset.filter(author__human=human) | queryset.filter(co_authors__human=human)\n-        )\n+        human = getattr(request.user, 'human', None)\n+        if human:\n+            return queryset.filter(\n+                pk__in=(queryset.filter(investigator_human=human) | queryset.filter(collaborators__human=human) |\n+                        queryset.filter(editor=request.user))\n+            )\n+        else:\n+            return queryset.filter(editor=request.user)\n+\n+    def save_model(self, request, obj, form, change):\n+        \"\"\"\n+        The `editor` can be changed only by user with change permission to grants but `editor` field cannot be null.\n+        Sets editor to the current user if it is a new `grant`.\n+        \"\"\"","path":"fuuk/people/admin/options.py","position":null,"original_position":141,"commit_id":"e87c09191321b9a60a4490ebe37552ba653c92c9","original_commit_id":"f4438ba86baafe793f703a9cf9219b7bb3e945b8","user":{"login":"lumik","id":443945,"avatar_url":"https://avatars.githubusercontent.com/u/443945?v=3","gravatar_id":"","url":"https://api.github.com/users/lumik","html_url":"https://github.com/lumik","followers_url":"https://api.github.com/users/lumik/followers","following_url":"https://api.github.com/users/lumik/following{/other_user}","gists_url":"https://api.github.com/users/lumik/gists{/gist_id}","starred_url":"https://api.github.com/users/lumik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lumik/subscriptions","organizations_url":"https://api.github.com/users/lumik/orgs","repos_url":"https://api.github.com/users/lumik/repos","events_url":"https://api.github.com/users/lumik/events{/privacy}","received_events_url":"https://api.github.com/users/lumik/received_events","type":"User","site_admin":false},"body":"Corrected.","created_at":"2016-09-13T14:23:02Z","updated_at":"2016-09-13T14:23:02Z","html_url":"https://github.com/FUUK/fuuk/pull/101#discussion_r78569353","pull_request_url":"https://api.github.com/repos/FUUK/fuuk/pulls/101","_links":{"self":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/comments/78569353"},"html":{"href":"https://github.com/FUUK/fuuk/pull/101#discussion_r78569353"},"pull_request":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/101"}}},"pull_request":{"url":"https://api.github.com/repos/FUUK/fuuk/pulls/101","id":74790737,"html_url":"https://github.com/FUUK/fuuk/pull/101","diff_url":"https://github.com/FUUK/fuuk/pull/101.diff","patch_url":"https://github.com/FUUK/fuuk/pull/101.patch","issue_url":"https://api.github.com/repos/FUUK/fuuk/issues/101","number":101,"state":"open","locked":false,"title":"Fixes #71: Refactor Grant authors","user":{"login":"lumik","id":443945,"avatar_url":"https://avatars.githubusercontent.com/u/443945?v=3","gravatar_id":"","url":"https://api.github.com/users/lumik","html_url":"https://github.com/lumik","followers_url":"https://api.github.com/users/lumik/followers","following_url":"https://api.github.com/users/lumik/following{/other_user}","gists_url":"https://api.github.com/users/lumik/gists{/gist_id}","starred_url":"https://api.github.com/users/lumik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lumik/subscriptions","organizations_url":"https://api.github.com/users/lumik/orgs","repos_url":"https://api.github.com/users/lumik/repos","events_url":"https://api.github.com/users/lumik/events{/privacy}","received_events_url":"https://api.github.com/users/lumik/received_events","type":"User","site_admin":false},"body":"Close #71.","created_at":"2016-06-22T14:09:49Z","updated_at":"2016-09-13T14:23:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":"dfc98b313c795b92109f8fb2b436601e2bb5d500","assignee":{"login":"lumik","id":443945,"avatar_url":"https://avatars.githubusercontent.com/u/443945?v=3","gravatar_id":"","url":"https://api.github.com/users/lumik","html_url":"https://github.com/lumik","followers_url":"https://api.github.com/users/lumik/followers","following_url":"https://api.github.com/users/lumik/following{/other_user}","gists_url":"https://api.github.com/users/lumik/gists{/gist_id}","starred_url":"https://api.github.com/users/lumik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lumik/subscriptions","organizations_url":"https://api.github.com/users/lumik/orgs","repos_url":"https://api.github.com/users/lumik/repos","events_url":"https://api.github.com/users/lumik/events{/privacy}","received_events_url":"https://api.github.com/users/lumik/received_events","type":"User","site_admin":false},"assignees":[{"login":"lumik","id":443945,"avatar_url":"https://avatars.githubusercontent.com/u/443945?v=3","gravatar_id":"","url":"https://api.github.com/users/lumik","html_url":"https://github.com/lumik","followers_url":"https://api.github.com/users/lumik/followers","following_url":"https://api.github.com/users/lumik/following{/other_user}","gists_url":"https://api.github.com/users/lumik/gists{/gist_id}","starred_url":"https://api.github.com/users/lumik/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lumik/subscriptions","organizations_url":"https://api.github.com/users/lumik/orgs","repos_url":"https://api.github.com/users/lumik/repos","events_url":"https://api.github.com/users/lumik/events{/privacy}","received_events_url":"https://api.github.com/users/lumik/received_events","type":"User","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/FUUK/fuuk/milestones/11","html_url":"https://github.com/FUUK/fuuk/milestones/0.15","labels_url":"https://api.github.com/repos/FUUK/fuuk/milestones/11/labels","id":1988608,"number":11,"title":"0.15","description":"","creator":{"login":"ziima","id":331955,"avatar_url":"https://avatars.githubusercontent.com/u/331955?v=3","gravatar_id":"","url":"https://api.github.com/users/ziima","html_url":"https://github.com/ziima","followers_url":"https://api.github.com/users/ziima/followers","following_url":"https://api.github.com/users/ziima/following{/other_user}","gists_url":"https://api.github.com/users/ziima/gists{/gist_id}","starred_url":"https://api.github.com/users/ziima/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ziima/subscriptions","organizations_url":"https://api.github.com/users/ziima/orgs","repos_url":"https://api.github.com/users/ziima/repos","events_url":"https://api.github.com/users/ziima/events{/privacy}","received_events_url":"https://api.github.com/users/ziima/received_events","type":"User","site_admin":false},"open_issues":5,"closed_issues":0,"state":"open","created_at":"2016-09-07T11:51:35Z","updated_at":"2016-09-13T10:40:56Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/FUUK/fuuk/pulls/101/commits","review_comments_url":"https://api.github.com/repos/FUUK/fuuk/pulls/101/comments","review_comment_url":"https://api.github.com/repos/FUUK/fuuk/pulls/comments{/number}","comments_url":"https://api.github.com/repos/FUUK/fuuk/issues/101/comments","statuses_url":"https://api.github.com/repos/FUUK/fuuk/statuses/e87c09191321b9a60a4490ebe37552ba653c92c9","head":{"label":"FUUK:71_refactor_grant_authors","ref":"71_refactor_grant_authors","sha":"e87c09191321b9a60a4490ebe37552ba653c92c9","user":{"login":"FUUK","id":6593947,"avatar_url":"https://avatars.githubusercontent.com/u/6593947?v=3","gravatar_id":"","url":"https://api.github.com/users/FUUK","html_url":"https://github.com/FUUK","followers_url":"https://api.github.com/users/FUUK/followers","following_url":"https://api.github.com/users/FUUK/following{/other_user}","gists_url":"https://api.github.com/users/FUUK/gists{/gist_id}","starred_url":"https://api.github.com/users/FUUK/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FUUK/subscriptions","organizations_url":"https://api.github.com/users/FUUK/orgs","repos_url":"https://api.github.com/users/FUUK/repos","events_url":"https://api.github.com/users/FUUK/events{/privacy}","received_events_url":"https://api.github.com/users/FUUK/received_events","type":"Organization","site_admin":false},"repo":{"id":1008705,"name":"fuuk","full_name":"FUUK/fuuk","owner":{"login":"FUUK","id":6593947,"avatar_url":"https://avatars.githubusercontent.com/u/6593947?v=3","gravatar_id":"","url":"https://api.github.com/users/FUUK","html_url":"https://github.com/FUUK","followers_url":"https://api.github.com/users/FUUK/followers","following_url":"https://api.github.com/users/FUUK/following{/other_user}","gists_url":"https://api.github.com/users/FUUK/gists{/gist_id}","starred_url":"https://api.github.com/users/FUUK/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FUUK/subscriptions","organizations_url":"https://api.github.com/users/FUUK/orgs","repos_url":"https://api.github.com/users/FUUK/repos","events_url":"https://api.github.com/users/FUUK/events{/privacy}","received_events_url":"https://api.github.com/users/FUUK/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/FUUK/fuuk","description":"Web of FÚUK","fork":false,"url":"https://api.github.com/repos/FUUK/fuuk","forks_url":"https://api.github.com/repos/FUUK/fuuk/forks","keys_url":"https://api.github.com/repos/FUUK/fuuk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/FUUK/fuuk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/FUUK/fuuk/teams","hooks_url":"https://api.github.com/repos/FUUK/fuuk/hooks","issue_events_url":"https://api.github.com/repos/FUUK/fuuk/issues/events{/number}","events_url":"https://api.github.com/repos/FUUK/fuuk/events","assignees_url":"https://api.github.com/repos/FUUK/fuuk/assignees{/user}","branches_url":"https://api.github.com/repos/FUUK/fuuk/branches{/branch}","tags_url":"https://api.github.com/repos/FUUK/fuuk/tags","blobs_url":"https://api.github.com/repos/FUUK/fuuk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/FUUK/fuuk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/FUUK/fuuk/git/refs{/sha}","trees_url":"https://api.github.com/repos/FUUK/fuuk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/FUUK/fuuk/statuses/{sha}","languages_url":"https://api.github.com/repos/FUUK/fuuk/languages","stargazers_url":"https://api.github.com/repos/FUUK/fuuk/stargazers","contributors_url":"https://api.github.com/repos/FUUK/fuuk/contributors","subscribers_url":"https://api.github.com/repos/FUUK/fuuk/subscribers","subscription_url":"https://api.github.com/repos/FUUK/fuuk/subscription","commits_url":"https://api.github.com/repos/FUUK/fuuk/commits{/sha}","git_commits_url":"https://api.github.com/repos/FUUK/fuuk/git/commits{/sha}","comments_url":"https://api.github.com/repos/FUUK/fuuk/comments{/number}","issue_comment_url":"https://api.github.com/repos/FUUK/fuuk/issues/comments{/number}","contents_url":"https://api.github.com/repos/FUUK/fuuk/contents/{+path}","compare_url":"https://api.github.com/repos/FUUK/fuuk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/FUUK/fuuk/merges","archive_url":"https://api.github.com/repos/FUUK/fuuk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/FUUK/fuuk/downloads","issues_url":"https://api.github.com/repos/FUUK/fuuk/issues{/number}","pulls_url":"https://api.github.com/repos/FUUK/fuuk/pulls{/number}","milestones_url":"https://api.github.com/repos/FUUK/fuuk/milestones{/number}","notifications_url":"https://api.github.com/repos/FUUK/fuuk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/FUUK/fuuk/labels{/name}","releases_url":"https://api.github.com/repos/FUUK/fuuk/releases{/id}","deployments_url":"https://api.github.com/repos/FUUK/fuuk/deployments","created_at":"2010-10-20T10:22:58Z","updated_at":"2016-03-23T07:36:34Z","pushed_at":"2016-09-13T14:13:47Z","git_url":"git://github.com/FUUK/fuuk.git","ssh_url":"git@github.com:FUUK/fuuk.git","clone_url":"https://github.com/FUUK/fuuk.git","svn_url":"https://github.com/FUUK/fuuk","homepage":"","size":724,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":15,"forks":0,"open_issues":15,"watchers":3,"default_branch":"master"}},"base":{"label":"FUUK:master","ref":"master","sha":"bdeac4324e6cfd5980dc5cdabd609e97682c4a24","user":{"login":"FUUK","id":6593947,"avatar_url":"https://avatars.githubusercontent.com/u/6593947?v=3","gravatar_id":"","url":"https://api.github.com/users/FUUK","html_url":"https://github.com/FUUK","followers_url":"https://api.github.com/users/FUUK/followers","following_url":"https://api.github.com/users/FUUK/following{/other_user}","gists_url":"https://api.github.com/users/FUUK/gists{/gist_id}","starred_url":"https://api.github.com/users/FUUK/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FUUK/subscriptions","organizations_url":"https://api.github.com/users/FUUK/orgs","repos_url":"https://api.github.com/users/FUUK/repos","events_url":"https://api.github.com/users/FUUK/events{/privacy}","received_events_url":"https://api.github.com/users/FUUK/received_events","type":"Organization","site_admin":false},"repo":{"id":1008705,"name":"fuuk","full_name":"FUUK/fuuk","owner":{"login":"FUUK","id":6593947,"avatar_url":"https://avatars.githubusercontent.com/u/6593947?v=3","gravatar_id":"","url":"https://api.github.com/users/FUUK","html_url":"https://github.com/FUUK","followers_url":"https://api.github.com/users/FUUK/followers","following_url":"https://api.github.com/users/FUUK/following{/other_user}","gists_url":"https://api.github.com/users/FUUK/gists{/gist_id}","starred_url":"https://api.github.com/users/FUUK/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/FUUK/subscriptions","organizations_url":"https://api.github.com/users/FUUK/orgs","repos_url":"https://api.github.com/users/FUUK/repos","events_url":"https://api.github.com/users/FUUK/events{/privacy}","received_events_url":"https://api.github.com/users/FUUK/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/FUUK/fuuk","description":"Web of FÚUK","fork":false,"url":"https://api.github.com/repos/FUUK/fuuk","forks_url":"https://api.github.com/repos/FUUK/fuuk/forks","keys_url":"https://api.github.com/repos/FUUK/fuuk/keys{/key_id}","collaborators_url":"https://api.github.com/repos/FUUK/fuuk/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/FUUK/fuuk/teams","hooks_url":"https://api.github.com/repos/FUUK/fuuk/hooks","issue_events_url":"https://api.github.com/repos/FUUK/fuuk/issues/events{/number}","events_url":"https://api.github.com/repos/FUUK/fuuk/events","assignees_url":"https://api.github.com/repos/FUUK/fuuk/assignees{/user}","branches_url":"https://api.github.com/repos/FUUK/fuuk/branches{/branch}","tags_url":"https://api.github.com/repos/FUUK/fuuk/tags","blobs_url":"https://api.github.com/repos/FUUK/fuuk/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/FUUK/fuuk/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/FUUK/fuuk/git/refs{/sha}","trees_url":"https://api.github.com/repos/FUUK/fuuk/git/trees{/sha}","statuses_url":"https://api.github.com/repos/FUUK/fuuk/statuses/{sha}","languages_url":"https://api.github.com/repos/FUUK/fuuk/languages","stargazers_url":"https://api.github.com/repos/FUUK/fuuk/stargazers","contributors_url":"https://api.github.com/repos/FUUK/fuuk/contributors","subscribers_url":"https://api.github.com/repos/FUUK/fuuk/subscribers","subscription_url":"https://api.github.com/repos/FUUK/fuuk/subscription","commits_url":"https://api.github.com/repos/FUUK/fuuk/commits{/sha}","git_commits_url":"https://api.github.com/repos/FUUK/fuuk/git/commits{/sha}","comments_url":"https://api.github.com/repos/FUUK/fuuk/comments{/number}","issue_comment_url":"https://api.github.com/repos/FUUK/fuuk/issues/comments{/number}","contents_url":"https://api.github.com/repos/FUUK/fuuk/contents/{+path}","compare_url":"https://api.github.com/repos/FUUK/fuuk/compare/{base}...{head}","merges_url":"https://api.github.com/repos/FUUK/fuuk/merges","archive_url":"https://api.github.com/repos/FUUK/fuuk/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/FUUK/fuuk/downloads","issues_url":"https://api.github.com/repos/FUUK/fuuk/issues{/number}","pulls_url":"https://api.github.com/repos/FUUK/fuuk/pulls{/number}","milestones_url":"https://api.github.com/repos/FUUK/fuuk/milestones{/number}","notifications_url":"https://api.github.com/repos/FUUK/fuuk/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/FUUK/fuuk/labels{/name}","releases_url":"https://api.github.com/repos/FUUK/fuuk/releases{/id}","deployments_url":"https://api.github.com/repos/FUUK/fuuk/deployments","created_at":"2010-10-20T10:22:58Z","updated_at":"2016-03-23T07:36:34Z","pushed_at":"2016-09-13T14:13:47Z","git_url":"git://github.com/FUUK/fuuk.git","ssh_url":"git@github.com:FUUK/fuuk.git","clone_url":"https://github.com/FUUK/fuuk.git","svn_url":"https://github.com/FUUK/fuuk","homepage":"","size":724,"stargazers_count":3,"watchers_count":3,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":15,"forks":0,"open_issues":15,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/101"},"html":{"href":"https://github.com/FUUK/fuuk/pull/101"},"issue":{"href":"https://api.github.com/repos/FUUK/fuuk/issues/101"},"comments":{"href":"https://api.github.com/repos/FUUK/fuuk/issues/101/comments"},"review_comments":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/101/comments"},"review_comment":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/FUUK/fuuk/pulls/101/commits"},"statuses":{"href":"https://api.github.com/repos/FUUK/fuuk/statuses/e87c09191321b9a60a4490ebe37552ba653c92c9"}}}}
{"actor":{"display_login":"lumik"}}