diff --git a/taiga/base/filters.py b/taiga/base/filters.py index 187033c1..a30e2dcf 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -203,7 +203,7 @@ class PermissionBasedAttachmentFilterBackend(PermissionBasedFilterBackend): class CanViewEpicAttachmentFilterBackend(PermissionBasedAttachmentFilterBackend): - permission = "view_epic" + permission = "view_epics" class CanViewUserStoryAttachmentFilterBackend(PermissionBasedAttachmentFilterBackend): diff --git a/taiga/permissions/choices.py b/taiga/permissions/choices.py index 5cda50a5..3564acc8 100644 --- a/taiga/permissions/choices.py +++ b/taiga/permissions/choices.py @@ -22,7 +22,7 @@ from django.utils.translation import ugettext_lazy as _ ANON_PERMISSIONS = [ ('view_project', _('View project')), ('view_milestones', _('View milestones')), - ('view_epic', _('View epic')), + ('view_epics', _('View epic')), ('view_us', _('View user stories')), ('view_tasks', _('View tasks')), ('view_issues', _('View issues')), @@ -38,7 +38,7 @@ MEMBERS_PERMISSIONS = [ ('modify_milestone', _('Modify milestone')), ('delete_milestone', _('Delete milestone')), # US permissions - ('view_epic', _('View epic')), + ('view_epics', _('View epic')), ('add_epic', _('Add epic')), ('modify_epic', _('Modify epic')), ('comment_epic', _('Comment epic')), diff --git a/taiga/projects/attachments/permissions.py b/taiga/projects/attachments/permissions.py index ee768014..4c7a7915 100644 --- a/taiga/projects/attachments/permissions.py +++ b/taiga/projects/attachments/permissions.py @@ -29,7 +29,7 @@ class IsAttachmentOwnerPerm(PermissionComponent): class EpicAttachmentPermission(TaigaResourcePermission): - retrieve_perms = HasProjectPerm('view_epic') | IsAttachmentOwnerPerm() + retrieve_perms = HasProjectPerm('view_epics') | IsAttachmentOwnerPerm() create_perms = HasProjectPerm('modify_epic') update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm() partial_update_perms = HasProjectPerm('modify_epic') | IsAttachmentOwnerPerm() diff --git a/taiga/projects/epics/api.py b/taiga/projects/epics/api.py index 7b703ba5..e71106d1 100644 --- a/taiga/projects/epics/api.py +++ b/taiga/projects/epics/api.py @@ -178,9 +178,11 @@ class EpicViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, raise exc.Blocked(_("Blocked element")) epics = services.create_epics_in_bulk( - data["bulk_epics"], milestone_id=data["sprint_id"], user_story_id=data["us_id"], + data["bulk_epics"], status_id=data.get("status_id") or project.default_epic_status_id, - project=project, owner=request.user, callback=self.post_save, precall=self.pre_save) + project=project, + owner=request.user, + callback=self.post_save, precall=self.pre_save) epics = self.get_queryset().filter(id__in=[i.id for i in epics]) epics_serialized = self.get_serializer_class()(epics, many=True) @@ -209,8 +211,8 @@ class EpicViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, return response.NoContent() @list_route(methods=["POST"]) - def bulk_update_epic_order(self, request, **kwargs): - return self._bulk_update_order("epic_order", request, **kwargs) + def bulk_update_epics_order(self, request, **kwargs): + return self._bulk_update_order("epics_order", request, **kwargs) class EpicVotersViewSet(VotersViewSetMixin, ModelListViewSet): diff --git a/taiga/projects/epics/apps.py b/taiga/projects/epics/apps.py index 5389b98a..bf489ea0 100644 --- a/taiga/projects/epics/apps.py +++ b/taiga/projects/epics/apps.py @@ -43,7 +43,7 @@ def connect_all_epics_signals(): def disconnect_epics_signals(): - signals.pre_save.disconnect(sender=apps.get_model("epics", "Task"), + signals.pre_save.disconnect(sender=apps.get_model("epics", "Epic"), dispatch_uid="tags_normalization") diff --git a/taiga/projects/epics/serializers.py b/taiga/projects/epics/serializers.py index d9d45b2b..4118553e 100644 --- a/taiga/projects/epics/serializers.py +++ b/taiga/projects/epics/serializers.py @@ -40,7 +40,7 @@ class EpicListSerializer(VoteResourceSerializerMixin, WatchedResourceSerializer, created_date = Field() modified_date = Field() subject = Field() - epic_order = Field() + epics_order = Field() client_requirement = Field() team_requirement = Field() version = Field() diff --git a/taiga/projects/fixtures/initial_project_templates.json b/taiga/projects/fixtures/initial_project_templates.json index 83d64b80..5c711c00 100644 --- a/taiga/projects/fixtures/initial_project_templates.json +++ b/taiga/projects/fixtures/initial_project_templates.json @@ -8,7 +8,7 @@ "description": "The agile product backlog in Scrum is a prioritized features list, containing short descriptions of all functionality desired in the product. When applying Scrum, it's not necessary to start a project with a lengthy, upfront effort to document all requirements. The Scrum product backlog is then allowed to grow and change as more is learned about the product and its customers", "order": 1, "created_date": "2014-04-22T14:48:43.596Z", - "modified_date": "2016-06-29T14:52:11.273Z", + "modified_date": "2016-07-07T13:18:25.350Z", "default_owner_role": "product-owner", "is_epics_activated": true, "is_backlog_activated": true, @@ -17,16 +17,16 @@ "is_issues_activated": true, "videoconferences": null, "videoconferences_extra_data": "", - "default_options": "{\"epic_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"us_status\": \"New\", \"points\": \"?\", \"priority\": \"Normal\", \"task_status\": \"New\", \"issue_status\": \"New\"}", - "epic_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"order\": 5, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}]", - "us_statuses": "[{\"name\": \"New\", \"is_archived\": false, \"wip_limit\": null, \"order\": 1, \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"name\": \"Ready\", \"is_archived\": false, \"wip_limit\": null, \"order\": 2, \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"name\": \"In progress\", \"is_archived\": false, \"wip_limit\": null, \"order\": 3, \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"name\": \"Ready for test\", \"is_archived\": false, \"wip_limit\": null, \"order\": 4, \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"name\": \"Done\", \"is_archived\": false, \"wip_limit\": null, \"order\": 5, \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}, {\"name\": \"Archived\", \"is_archived\": true, \"wip_limit\": null, \"order\": 6, \"color\": \"#5c3566\", \"slug\": \"archived\", \"is_closed\": true}]", - "points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]", - "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#ffcc00\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#669900\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#999999\", \"slug\": \"needs-info\", \"is_closed\": false}]", - "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#8C2318\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#5E8C6A\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#88A65E\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#BFB35A\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#89BAB4\", \"slug\": \"needs-info\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#CC0000\", \"slug\": \"rejected\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#666666\", \"slug\": \"posponed\", \"is_closed\": false}]", - "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#89BAB4\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#ba89a8\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#89a8ba\"}]", - "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#666666\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#669933\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", - "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#666666\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#669933\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#0000FF\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#FFA500\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", - "roles": "[{\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false}]" + "default_options": "{\"issue_status\": \"New\", \"task_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"points\": \"?\", \"priority\": \"Normal\", \"us_status\": \"New\", \"epic_status\": \"New\"}", + "epic_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"Ready\", \"is_closed\": false, \"slug\": \"ready\", \"order\": 2, \"color\": \"#ff8a84\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 3, \"color\": \"#ff9900\"}, {\"name\": \"Ready for test\", \"is_closed\": false, \"slug\": \"ready-for-test\", \"order\": 4, \"color\": \"#fcc000\"}, {\"name\": \"Done\", \"is_closed\": true, \"slug\": \"done\", \"order\": 5, \"color\": \"#669900\"}]", + "us_statuses": "[{\"wip_limit\": null, \"name\": \"New\", \"slug\": \"new\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#999999\", \"order\": 1}, {\"wip_limit\": null, \"name\": \"Ready\", \"slug\": \"ready\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#ff8a84\", \"order\": 2}, {\"wip_limit\": null, \"name\": \"In progress\", \"slug\": \"in-progress\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#ff9900\", \"order\": 3}, {\"wip_limit\": null, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#fcc000\", \"order\": 4}, {\"wip_limit\": null, \"name\": \"Done\", \"slug\": \"done\", \"is_closed\": true, \"is_archived\": false, \"color\": \"#669900\", \"order\": 5}, {\"wip_limit\": null, \"name\": \"Archived\", \"slug\": \"archived\", \"is_closed\": true, \"is_archived\": true, \"color\": \"#5c3566\", \"order\": 6}]", + "points": "[{\"name\": \"?\", \"value\": null, \"order\": 1}, {\"name\": \"0\", \"value\": 0.0, \"order\": 2}, {\"name\": \"1/2\", \"value\": 0.5, \"order\": 3}, {\"name\": \"1\", \"value\": 1.0, \"order\": 4}, {\"name\": \"2\", \"value\": 2.0, \"order\": 5}, {\"name\": \"3\", \"value\": 3.0, \"order\": 6}, {\"name\": \"5\", \"value\": 5.0, \"order\": 7}, {\"name\": \"8\", \"value\": 8.0, \"order\": 8}, {\"name\": \"10\", \"value\": 10.0, \"order\": 9}, {\"name\": \"13\", \"value\": 13.0, \"order\": 10}, {\"name\": \"20\", \"value\": 20.0, \"order\": 11}, {\"name\": \"40\", \"value\": 40.0, \"order\": 12}]", + "task_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 2, \"color\": \"#ff9900\"}, {\"name\": \"Ready for test\", \"is_closed\": true, \"slug\": \"ready-for-test\", \"order\": 3, \"color\": \"#ffcc00\"}, {\"name\": \"Closed\", \"is_closed\": true, \"slug\": \"closed\", \"order\": 4, \"color\": \"#669900\"}, {\"name\": \"Needs Info\", \"is_closed\": false, \"slug\": \"needs-info\", \"order\": 5, \"color\": \"#999999\"}]", + "issue_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#8C2318\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 2, \"color\": \"#5E8C6A\"}, {\"name\": \"Ready for test\", \"is_closed\": true, \"slug\": \"ready-for-test\", \"order\": 3, \"color\": \"#88A65E\"}, {\"name\": \"Closed\", \"is_closed\": true, \"slug\": \"closed\", \"order\": 4, \"color\": \"#BFB35A\"}, {\"name\": \"Needs Info\", \"is_closed\": false, \"slug\": \"needs-info\", \"order\": 5, \"color\": \"#89BAB4\"}, {\"name\": \"Rejected\", \"is_closed\": true, \"slug\": \"rejected\", \"order\": 6, \"color\": \"#CC0000\"}, {\"name\": \"Postponed\", \"is_closed\": false, \"slug\": \"posponed\", \"order\": 7, \"color\": \"#666666\"}]", + "issue_types": "[{\"name\": \"Bug\", \"order\": 1, \"color\": \"#89BAB4\"}, {\"name\": \"Question\", \"order\": 2, \"color\": \"#ba89a8\"}, {\"name\": \"Enhancement\", \"order\": 3, \"color\": \"#89a8ba\"}]", + "priorities": "[{\"name\": \"Low\", \"order\": 1, \"color\": \"#666666\"}, {\"name\": \"Normal\", \"order\": 3, \"color\": \"#669933\"}, {\"name\": \"High\", \"order\": 5, \"color\": \"#CC0000\"}]", + "severities": "[{\"name\": \"Wishlist\", \"order\": 1, \"color\": \"#666666\"}, {\"name\": \"Minor\", \"order\": 2, \"color\": \"#669933\"}, {\"name\": \"Normal\", \"order\": 3, \"color\": \"#0000FF\"}, {\"name\": \"Important\", \"order\": 4, \"color\": \"#FFA500\"}, {\"name\": \"Critical\", \"order\": 5, \"color\": \"#CC0000\"}]", + "roles": "[{\"name\": \"UX\", \"computable\": true, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 10}, {\"name\": \"Design\", \"computable\": true, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 20}, {\"name\": \"Front\", \"computable\": true, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 30}, {\"name\": \"Back\", \"computable\": true, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 40}, {\"name\": \"Product Owner\", \"computable\": false, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 50}, {\"name\": \"Stakeholder\", \"computable\": false, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\"], \"order\": 60}]" } }, { @@ -38,7 +38,7 @@ "description": "Kanban is a method for managing knowledge work with an emphasis on just-in-time delivery while not overloading the team members. In this approach, the process, from definition of a task to its delivery to the customer, is displayed for participants to see and team members pull work from a queue.", "order": 2, "created_date": "2014-04-22T14:50:19.738Z", - "modified_date": "2016-06-29T14:52:15.232Z", + "modified_date": "2016-07-07T13:18:28.186Z", "default_owner_role": "product-owner", "is_epics_activated": true, "is_backlog_activated": false, @@ -47,16 +47,16 @@ "is_issues_activated": false, "videoconferences": null, "videoconferences_extra_data": "", - "default_options": "{\"epic_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"us_status\": \"New\", \"points\": \"?\", \"priority\": \"Normal\", \"task_status\": \"New\", \"issue_status\": \"New\"}", - "epic_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"Ready\", \"color\": \"#ff8a84\", \"slug\": \"ready\", \"is_closed\": false}, {\"order\": 3, \"name\": \"In progress\", \"color\": \"#ff9900\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 4, \"name\": \"Ready for test\", \"color\": \"#fcc000\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"order\": 5, \"name\": \"Done\", \"color\": \"#669900\", \"slug\": \"done\", \"is_closed\": true}]", - "us_statuses": "[{\"name\": \"New\", \"is_archived\": false, \"wip_limit\": null, \"order\": 1, \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"name\": \"Ready\", \"is_archived\": false, \"wip_limit\": null, \"order\": 2, \"color\": \"#f57900\", \"slug\": \"ready\", \"is_closed\": false}, {\"name\": \"In progress\", \"is_archived\": false, \"wip_limit\": null, \"order\": 3, \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"name\": \"Ready for test\", \"is_archived\": false, \"wip_limit\": null, \"order\": 4, \"color\": \"#4e9a06\", \"slug\": \"ready-for-test\", \"is_closed\": false}, {\"name\": \"Done\", \"is_archived\": false, \"wip_limit\": null, \"order\": 5, \"color\": \"#cc0000\", \"slug\": \"done\", \"is_closed\": true}, {\"name\": \"Archived\", \"is_archived\": true, \"wip_limit\": null, \"order\": 6, \"color\": \"#5c3566\", \"slug\": \"archived\", \"is_closed\": true}]", - "points": "[{\"order\": 1, \"name\": \"?\", \"value\": null}, {\"order\": 2, \"name\": \"0\", \"value\": 0.0}, {\"order\": 3, \"name\": \"1/2\", \"value\": 0.5}, {\"order\": 4, \"name\": \"1\", \"value\": 1.0}, {\"order\": 5, \"name\": \"2\", \"value\": 2.0}, {\"order\": 6, \"name\": \"3\", \"value\": 3.0}, {\"order\": 7, \"name\": \"5\", \"value\": 5.0}, {\"order\": 8, \"name\": \"8\", \"value\": 8.0}, {\"order\": 9, \"name\": \"10\", \"value\": 10.0}, {\"order\": 10, \"name\": \"13\", \"value\": 13.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]", - "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"is_closed\": false}]", - "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"slug\": \"new\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#729fcf\", \"slug\": \"in-progress\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#f57900\", \"slug\": \"ready-for-test\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#4e9a06\", \"slug\": \"closed\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#cc0000\", \"slug\": \"needs-info\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#d3d7cf\", \"slug\": \"rejected\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#75507b\", \"slug\": \"posponed\", \"is_closed\": false}]", - "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#cc0000\"}, {\"order\": 2, \"name\": \"Question\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Enhancement\", \"color\": \"#4e9a06\"}]", - "priorities": "[{\"order\": 1, \"name\": \"Low\", \"color\": \"#999999\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 5, \"name\": \"High\", \"color\": \"#CC0000\"}]", - "severities": "[{\"order\": 1, \"name\": \"Wishlist\", \"color\": \"#999999\"}, {\"order\": 2, \"name\": \"Minor\", \"color\": \"#729fcf\"}, {\"order\": 3, \"name\": \"Normal\", \"color\": \"#4e9a06\"}, {\"order\": 4, \"name\": \"Important\", \"color\": \"#f57900\"}, {\"order\": 5, \"name\": \"Critical\", \"color\": \"#CC0000\"}]", - "roles": "[{\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 10, \"name\": \"UX\", \"slug\": \"ux\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 20, \"name\": \"Design\", \"slug\": \"design\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 30, \"name\": \"Front\", \"slug\": \"front\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 40, \"name\": \"Back\", \"slug\": \"back\", \"computable\": true}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 50, \"name\": \"Product Owner\", \"slug\": \"product-owner\", \"computable\": false}, {\"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\"], \"order\": 60, \"name\": \"Stakeholder\", \"slug\": \"stakeholder\", \"computable\": false}]" + "default_options": "{\"issue_status\": \"New\", \"task_status\": \"New\", \"severity\": \"Normal\", \"issue_type\": \"Bug\", \"points\": \"?\", \"priority\": \"Normal\", \"us_status\": \"New\", \"epic_status\": \"New\"}", + "epic_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"Ready\", \"is_closed\": false, \"slug\": \"ready\", \"order\": 2, \"color\": \"#ff8a84\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 3, \"color\": \"#ff9900\"}, {\"name\": \"Ready for test\", \"is_closed\": false, \"slug\": \"ready-for-test\", \"order\": 4, \"color\": \"#fcc000\"}, {\"name\": \"Done\", \"is_closed\": true, \"slug\": \"done\", \"order\": 5, \"color\": \"#669900\"}]", + "us_statuses": "[{\"wip_limit\": null, \"name\": \"New\", \"slug\": \"new\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#999999\", \"order\": 1}, {\"wip_limit\": null, \"name\": \"Ready\", \"slug\": \"ready\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#f57900\", \"order\": 2}, {\"wip_limit\": null, \"name\": \"In progress\", \"slug\": \"in-progress\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#729fcf\", \"order\": 3}, {\"wip_limit\": null, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\", \"is_closed\": false, \"is_archived\": false, \"color\": \"#4e9a06\", \"order\": 4}, {\"wip_limit\": null, \"name\": \"Done\", \"slug\": \"done\", \"is_closed\": true, \"is_archived\": false, \"color\": \"#cc0000\", \"order\": 5}, {\"wip_limit\": null, \"name\": \"Archived\", \"slug\": \"archived\", \"is_closed\": true, \"is_archived\": true, \"color\": \"#5c3566\", \"order\": 6}]", + "points": "[{\"name\": \"?\", \"value\": null, \"order\": 1}, {\"name\": \"0\", \"value\": 0.0, \"order\": 2}, {\"name\": \"1/2\", \"value\": 0.5, \"order\": 3}, {\"name\": \"1\", \"value\": 1.0, \"order\": 4}, {\"name\": \"2\", \"value\": 2.0, \"order\": 5}, {\"name\": \"3\", \"value\": 3.0, \"order\": 6}, {\"name\": \"5\", \"value\": 5.0, \"order\": 7}, {\"name\": \"8\", \"value\": 8.0, \"order\": 8}, {\"name\": \"10\", \"value\": 10.0, \"order\": 9}, {\"name\": \"13\", \"value\": 13.0, \"order\": 10}, {\"name\": \"20\", \"value\": 20.0, \"order\": 11}, {\"name\": \"40\", \"value\": 40.0, \"order\": 12}]", + "task_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 2, \"color\": \"#729fcf\"}, {\"name\": \"Ready for test\", \"is_closed\": true, \"slug\": \"ready-for-test\", \"order\": 3, \"color\": \"#f57900\"}, {\"name\": \"Closed\", \"is_closed\": true, \"slug\": \"closed\", \"order\": 4, \"color\": \"#4e9a06\"}, {\"name\": \"Needs Info\", \"is_closed\": false, \"slug\": \"needs-info\", \"order\": 5, \"color\": \"#cc0000\"}]", + "issue_statuses": "[{\"name\": \"New\", \"is_closed\": false, \"slug\": \"new\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"In progress\", \"is_closed\": false, \"slug\": \"in-progress\", \"order\": 2, \"color\": \"#729fcf\"}, {\"name\": \"Ready for test\", \"is_closed\": true, \"slug\": \"ready-for-test\", \"order\": 3, \"color\": \"#f57900\"}, {\"name\": \"Closed\", \"is_closed\": true, \"slug\": \"closed\", \"order\": 4, \"color\": \"#4e9a06\"}, {\"name\": \"Needs Info\", \"is_closed\": false, \"slug\": \"needs-info\", \"order\": 5, \"color\": \"#cc0000\"}, {\"name\": \"Rejected\", \"is_closed\": true, \"slug\": \"rejected\", \"order\": 6, \"color\": \"#d3d7cf\"}, {\"name\": \"Postponed\", \"is_closed\": false, \"slug\": \"posponed\", \"order\": 7, \"color\": \"#75507b\"}]", + "issue_types": "[{\"name\": \"Bug\", \"order\": 1, \"color\": \"#cc0000\"}, {\"name\": \"Question\", \"order\": 2, \"color\": \"#729fcf\"}, {\"name\": \"Enhancement\", \"order\": 3, \"color\": \"#4e9a06\"}]", + "priorities": "[{\"name\": \"Low\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"Normal\", \"order\": 3, \"color\": \"#4e9a06\"}, {\"name\": \"High\", \"order\": 5, \"color\": \"#CC0000\"}]", + "severities": "[{\"name\": \"Wishlist\", \"order\": 1, \"color\": \"#999999\"}, {\"name\": \"Minor\", \"order\": 2, \"color\": \"#729fcf\"}, {\"name\": \"Normal\", \"order\": 3, \"color\": \"#4e9a06\"}, {\"name\": \"Important\", \"order\": 4, \"color\": \"#f57900\"}, {\"name\": \"Critical\", \"order\": 5, \"color\": \"#CC0000\"}]", + "roles": "[{\"name\": \"UX\", \"computable\": true, \"slug\": \"ux\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 10}, {\"name\": \"Design\", \"computable\": true, \"slug\": \"design\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 20}, {\"name\": \"Front\", \"computable\": true, \"slug\": \"front\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 30}, {\"name\": \"Back\", \"computable\": true, \"slug\": \"back\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 40}, {\"name\": \"Product Owner\", \"computable\": false, \"slug\": \"product-owner\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"add_milestone\", \"modify_milestone\", \"delete_milestone\", \"view_milestones\", \"view_project\", \"add_task\", \"modify_task\", \"delete_task\", \"view_tasks\", \"add_us\", \"modify_us\", \"delete_us\", \"view_us\", \"add_wiki_page\", \"modify_wiki_page\", \"delete_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\", \"add_epic\", \"modify_epic\", \"delete_epic\"], \"order\": 50}, {\"name\": \"Stakeholder\", \"computable\": false, \"slug\": \"stakeholder\", \"permissions\": [\"add_issue\", \"modify_issue\", \"delete_issue\", \"view_issues\", \"view_milestones\", \"view_project\", \"view_tasks\", \"view_us\", \"modify_wiki_page\", \"view_wiki_pages\", \"add_wiki_link\", \"delete_wiki_link\", \"view_wiki_links\", \"view_epics\"], \"order\": 60}]" } } ] diff --git a/taiga/projects/migrations/0049_auto_20160629_1443.py b/taiga/projects/migrations/0049_auto_20160629_1443.py index cdfd420b..417875e5 100644 --- a/taiga/projects/migrations/0049_auto_20160629_1443.py +++ b/taiga/projects/migrations/0049_auto_20160629_1443.py @@ -81,12 +81,12 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='project', name='anon_permissions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_epic', 'View epic'), ('view_us', 'View user stories'), ('view_tasks', 'View tasks'), ('view_issues', 'View issues'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links')]), blank=True, default=[], null=True, size=None, verbose_name='anonymous permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('view_epics', 'View epic'), ('view_us', 'View user stories'), ('view_tasks', 'View tasks'), ('view_issues', 'View issues'), ('view_wiki_pages', 'View wiki pages'), ('view_wiki_links', 'View wiki links')]), blank=True, default=[], null=True, size=None, verbose_name='anonymous permissions'), ), migrations.AlterField( model_name='project', name='public_permissions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_epic', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_epics', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='user permissions'), ), migrations.AddField( model_name='epicstatus', diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index 06964eb3..94d37c80 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -160,7 +160,7 @@ def filter_timeline_for_user(timeline, user): content_types = { "view_project": ContentType.objects.get_by_natural_key("projects", "project"), "view_milestones": ContentType.objects.get_by_natural_key("milestones", "milestone"), - "view_epic": ContentType.objects.get_by_natural_key("epics", "epic"), + "view_epics": ContentType.objects.get_by_natural_key("epics", "epic"), "view_us": ContentType.objects.get_by_natural_key("userstories", "userstory"), "view_tasks": ContentType.objects.get_by_natural_key("tasks", "task"), "view_issues": ContentType.objects.get_by_natural_key("issues", "issue"), diff --git a/taiga/users/migrations/0022_auto_20160629_1443.py b/taiga/users/migrations/0022_auto_20160629_1443.py index 2acaf944..68a65443 100644 --- a/taiga/users/migrations/0022_auto_20160629_1443.py +++ b/taiga/users/migrations/0022_auto_20160629_1443.py @@ -16,6 +16,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='role', name='permissions', - field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_epic', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions'), + field=django.contrib.postgres.fields.ArrayField(base_field=models.TextField(choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_epics', 'View epic'), ('add_epic', 'Add epic'), ('modify_epic', 'Modify epic'), ('comment_epic', 'Comment epic'), ('delete_epic', 'Delete epic'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('comment_us', 'Comment user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('comment_task', 'Comment task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('comment_issue', 'Comment issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('comment_wiki_page', 'Comment wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')]), blank=True, default=[], null=True, size=None, verbose_name='permissions'), ), ] diff --git a/tests/integration/resources_permissions/test_epics_resources.py b/tests/integration/resources_permissions/test_epics_resources.py new file mode 100644 index 00000000..ceffe206 --- /dev/null +++ b/tests/integration/resources_permissions/test_epics_resources.py @@ -0,0 +1,901 @@ +# -*- coding: utf-8 -*- +# Copyright (C) 2014-2016 Andrey Antukh +# Copyright (C) 2014-2016 Jesús Espino +# Copyright (C) 2014-2016 David Barragán +# Copyright (C) 2014-2016 Alejandro Alonso +# Copyright (C) 2014-2016 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import uuid + +from django.core.urlresolvers import reverse + +from taiga.base.utils import json +from taiga.projects import choices as project_choices +from taiga.projects.models import Project +from taiga.projects.epics.serializers import EpicSerializer +from taiga.projects.epics.models import Epic +from taiga.projects.epics.utils import attach_extra_info as attach_epic_extra_info +from taiga.projects.utils import attach_extra_info as attach_project_extra_info +from taiga.permissions.choices import MEMBERS_PERMISSIONS, ANON_PERMISSIONS +from taiga.projects.occ import OCCResourceMixin + +from tests import factories as f +from tests.utils import helper_test_http_method, reconnect_signals +from taiga.projects.votes.services import add_vote +from taiga.projects.notifications.services import add_watcher + +from unittest import mock + +import pytest +pytestmark = pytest.mark.django_db + + +def setup_function(function): + reconnect_signals() + + +@pytest.fixture +def data(): + m = type("Models", (object,), {}) + + m.registered_user = f.UserFactory.create() + m.project_member_with_perms = f.UserFactory.create() + m.project_member_without_perms = f.UserFactory.create() + m.project_owner = f.UserFactory.create() + m.other_user = f.UserFactory.create() + + m.public_project = f.ProjectFactory(is_private=False, + anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), + owner=m.project_owner) + #epics_csv_uuid=uuid.uuid4().hex) + m.public_project = attach_project_extra_info(Project.objects.all()).get(id=m.public_project.id) + + m.private_project1 = f.ProjectFactory(is_private=True, + anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), + owner=m.project_owner) + #epics_csv_uuid=uuid.uuid4().hex) + m.private_project1 = attach_project_extra_info(Project.objects.all()).get(id=m.private_project1.id) + + m.private_project2 = f.ProjectFactory(is_private=True, + anon_permissions=[], + public_permissions=[], + owner=m.project_owner) + #epics_csv_uuid=uuid.uuid4().hex) + m.private_project2 = attach_project_extra_info(Project.objects.all()).get(id=m.private_project2.id) + + m.blocked_project = f.ProjectFactory(is_private=True, + anon_permissions=[], + public_permissions=[], + owner=m.project_owner, + #epics_csv_uuid=uuid.uuid4().hex, + blocked_code=project_choices.BLOCKED_BY_STAFF) + m.blocked_project = attach_project_extra_info(Project.objects.all()).get(id=m.blocked_project.id) + + m.public_membership = f.MembershipFactory( + project=m.public_project, + user=m.project_member_with_perms, + role__project=m.public_project, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + m.private_membership1 = f.MembershipFactory( + project=m.private_project1, + user=m.project_member_with_perms, + role__project=m.private_project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory( + project=m.private_project1, + user=m.project_member_without_perms, + role__project=m.private_project1, + role__permissions=[]) + m.private_membership2 = f.MembershipFactory( + project=m.private_project2, + user=m.project_member_with_perms, + role__project=m.private_project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory( + project=m.private_project2, + user=m.project_member_without_perms, + role__project=m.private_project2, + role__permissions=[]) + m.blocked_membership = f.MembershipFactory( + project=m.blocked_project, + user=m.project_member_with_perms, + role__project=m.blocked_project, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=m.blocked_project, + user=m.project_member_without_perms, + role__project=m.blocked_project, + role__permissions=[]) + + f.MembershipFactory(project=m.public_project, + user=m.project_owner, + is_admin=True) + + f.MembershipFactory(project=m.private_project1, + user=m.project_owner, + is_admin=True) + + f.MembershipFactory(project=m.private_project2, + user=m.project_owner, + is_admin=True) + + f.MembershipFactory(project=m.blocked_project, + user=m.project_owner, + is_admin=True) + + m.public_epic = f.EpicFactory(project=m.public_project, + status__project=m.public_project) + m.public_epic = attach_epic_extra_info(Epic.objects.all()).get(id=m.public_epic.id) + + m.private_epic1 = f.EpicFactory(project=m.private_project1, + status__project=m.private_project1) + m.private_epic1 = attach_epic_extra_info(Epic.objects.all()).get(id=m.private_epic1.id) + + m.private_epic2 = f.EpicFactory(project=m.private_project2, + status__project=m.private_project2) + m.private_epic2 = attach_epic_extra_info(Epic.objects.all()).get(id=m.private_epic2.id) + + m.blocked_epic = f.EpicFactory(project=m.blocked_project, + status__project=m.blocked_project) + m.blocked_epic = attach_epic_extra_info(Epic.objects.all()).get(id=m.blocked_epic.id) + + m.public_project.default_epic_status = m.public_epic.status + m.public_project.save() + m.private_project1.default_epic_status = m.private_epic1.status + m.private_project1.save() + m.private_project2.default_epic_status = m.private_epic2.status + m.private_project2.save() + m.blocked_project.default_epic_status = m.blocked_epic.status + m.blocked_project.save() + + return m + + +def test_epic_list(client, data): + url = reverse('epics-list') + + response = client.get(url) + epics_data = json.loads(response.content.decode('utf-8')) + assert len(epics_data) == 2 + assert response.status_code == 200 + + client.login(data.registered_user) + + response = client.get(url) + epics_data = json.loads(response.content.decode('utf-8')) + assert len(epics_data) == 2 + assert response.status_code == 200 + + client.login(data.project_member_with_perms) + + response = client.get(url) + epics_data = json.loads(response.content.decode('utf-8')) + assert len(epics_data) == 4 + assert response.status_code == 200 + + client.login(data.project_owner) + + response = client.get(url) + epics_data = json.loads(response.content.decode('utf-8')) + assert len(epics_data) == 4 + assert response.status_code == 200 + + +def test_epic_retrieve(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_epic_create(client, data): + url = reverse('epics-list') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + create_data = json.dumps({ + "subject": "test", + "ref": 1, + "project": data.public_project.pk, + "status": data.public_project.epic_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 2, + "project": data.private_project1.pk, + "status": data.private_project1.epic_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.private_project2.pk, + "status": data.private_project2.epic_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 201, 201] + + create_data = json.dumps({ + "subject": "test", + "ref": 3, + "project": data.blocked_project.pk, + "status": data.blocked_project.epic_statuses.all()[0].pk, + }) + results = helper_test_http_method(client, 'post', url, create_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_put_update(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + epic_data = EpicSerializer(data.public_epic).data + epic_data["subject"] = "test" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', public_url, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic1).data + epic_data["subject"] = "test" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url1, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic2).data + epic_data["subject"] = "test" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url2, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.blocked_epic).data + epic_data["subject"] = "test" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', blocked_url, epic_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_put_comment(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + epic_data = EpicSerializer(data.public_epic).data + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', public_url, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic1).data + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url1, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic2).data + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url2, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.blocked_epic).data + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', blocked_url, epic_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_put_update_and_comment(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + epic_data = EpicSerializer(data.public_epic).data + epic_data["subject"] = "test" + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', public_url, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic1).data + epic_data["subject"] = "test" + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url1, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.private_epic2).data + epic_data["subject"] = "test" + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', private_url2, epic_data, users) + assert results == [401, 403, 403, 200, 200] + + epic_data = EpicSerializer(data.blocked_epic).data + epic_data["subject"] = "test" + epic_data["comment"] = "test comment" + epic_data = json.dumps(epic_data) + results = helper_test_http_method(client, 'put', blocked_url, epic_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_put_update_with_project_change(client): + user1 = f.UserFactory.create() + user2 = f.UserFactory.create() + user3 = f.UserFactory.create() + user4 = f.UserFactory.create() + project1 = f.ProjectFactory() + project2 = f.ProjectFactory() + + epic_status1 = f.EpicStatusFactory.create(project=project1) + epic_status2 = f.EpicStatusFactory.create(project=project2) + + project1.default_epic_status = epic_status1 + project2.default_epic_status = epic_status2 + + project1.save() + project2.save() + + project1 = attach_project_extra_info(Project.objects.all()).get(id=project1.id) + project2 = attach_project_extra_info(Project.objects.all()).get(id=project2.id) + + f.MembershipFactory(project=project1, + user=user1, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=project2, + user=user1, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=project1, + user=user2, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + f.MembershipFactory(project=project2, + user=user3, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + epic = f.EpicFactory.create(project=project1) + epic = attach_epic_extra_info(Epic.objects.all()).get(id=epic.id) + + url = reverse('epics-detail', kwargs={"pk": epic.pk}) + + # Test user with permissions in both projects + client.login(user1) + + epic_data = EpicSerializer(epic).data + epic_data["project"] = project2.id + epic_data = json.dumps(epic_data) + + response = client.put(url, data=epic_data, content_type="application/json") + + assert response.status_code == 200 + + epic.project = project1 + epic.save() + + # Test user with permissions in only origin project + client.login(user2) + + epic_data = EpicSerializer(epic).data + epic_data["project"] = project2.id + epic_data = json.dumps(epic_data) + + response = client.put(url, data=epic_data, content_type="application/json") + + assert response.status_code == 403 + + epic.project = project1 + epic.save() + + # Test user with permissions in only destionation project + client.login(user3) + + epic_data = EpicSerializer(epic).data + epic_data["project"] = project2.id + epic_data = json.dumps(epic_data) + + response = client.put(url, data=epic_data, content_type="application/json") + + assert response.status_code == 403 + + epic.project = project1 + epic.save() + + # Test user without permissions in the projects + client.login(user4) + + epic_data = EpicSerializer(epic).data + epic_data["project"] = project2.id + epic_data = json.dumps(epic_data) + + response = client.put(url, data=epic_data, content_type="application/json") + + assert response.status_code == 403 + + epic.project = project1 + epic.save() + + +def test_epic_patch_update(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"subject": "test", "version": data.public_epic.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"subject": "test", "version": data.private_epic1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"subject": "test", "version": data.private_epic2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"subject": "test", "version": data.blocked_epic.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_patch_comment(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({"comment": "test comment", "version": data.public_epic.version}) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_epic1.version}) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.private_epic2.version}) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({"comment": "test comment", "version": data.blocked_epic.version}) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_patch_update_and_comment(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + with mock.patch.object(OCCResourceMixin, "_validate_and_update_version"): + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.public_epic.version + }) + results = helper_test_http_method(client, 'patch', public_url, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_epic1.version + }) + results = helper_test_http_method(client, 'patch', private_url1, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.private_epic2.version + }) + results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) + assert results == [401, 403, 403, 200, 200] + + patch_data = json.dumps({ + "subject": "test", + "comment": "test comment", + "version": data.blocked_epic.version + }) + results = helper_test_http_method(client, 'patch', blocked_url, patch_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_delete(client, data): + public_url = reverse('epics-detail', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-detail', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-detail', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-detail', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + ] + results = helper_test_http_method(client, 'delete', public_url, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url1, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', private_url2, None, users) + assert results == [401, 403, 403, 204] + results = helper_test_http_method(client, 'delete', blocked_url, None, users) + assert results == [401, 403, 403, 451] + + +def test_epic_action_bulk_create(client, data): + url = reverse('epics-bulk-create') + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + bulk_data = json.dumps({ + "bulk_epics": "test1\ntest2", + "project_id": data.public_epic.project.pk, + }) + results = helper_test_http_method(client, 'post', url, bulk_data, users) + assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_epics": "test1\ntest2", + "project_id": data.private_epic1.project.pk, + }) + results = helper_test_http_method(client, 'post', url, bulk_data, users) + assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_epics": "test1\ntest2", + "project_id": data.private_epic2.project.pk, + }) + results = helper_test_http_method(client, 'post', url, bulk_data, users) + assert results == [401, 403, 403, 200, 200] + + bulk_data = json.dumps({ + "bulk_epics": "test1\ntest2", + "project_id": data.blocked_epic.project.pk, + }) + results = helper_test_http_method(client, 'post', url, bulk_data, users) + assert results == [401, 403, 403, 451, 451] + + +def test_epic_action_upvote(client, data): + public_url = reverse('epics-upvote', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-upvote', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-upvote', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-upvote', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + results = helper_test_http_method(client, 'post', blocked_url, "", users) + assert results == [404, 404, 404, 451, 451] + + +def test_epic_action_downvote(client, data): + public_url = reverse('epics-downvote', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-downvote', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-downvote', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-downvote', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + results = helper_test_http_method(client, 'post', blocked_url, "", users) + assert results == [404, 404, 404, 451, 451] + + +def test_epic_voters_list(client, data): + public_url = reverse('epic-voters-list', kwargs={"resource_id": data.public_epic.pk}) + private_url1 = reverse('epic-voters-list', kwargs={"resource_id": data.private_epic1.pk}) + private_url2 = reverse('epic-voters-list', kwargs={"resource_id": data.private_epic2.pk}) + blocked_url = reverse('epic-voters-list', kwargs={"resource_id": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_epic_voters_retrieve(client, data): + add_vote(data.public_epic, data.project_owner) + public_url = reverse('epic-voters-detail', kwargs={"resource_id": data.public_epic.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_epic1, data.project_owner) + private_url1 = reverse('epic-voters-detail', kwargs={"resource_id": data.private_epic1.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_epic2, data.project_owner) + private_url2 = reverse('epic-voters-detail', kwargs={"resource_id": data.private_epic2.pk, + "pk": data.project_owner.pk}) + + add_vote(data.blocked_epic, data.project_owner) + blocked_url = reverse('epic-voters-detail', kwargs={"resource_id": data.blocked_epic.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_epic_action_watch(client, data): + public_url = reverse('epics-watch', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-watch', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-watch', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-watch', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + results = helper_test_http_method(client, 'post', blocked_url, "", users) + assert results == [404, 404, 404, 451, 451] + + +def test_epic_action_unwatch(client, data): + public_url = reverse('epics-unwatch', kwargs={"pk": data.public_epic.pk}) + private_url1 = reverse('epics-unwatch', kwargs={"pk": data.private_epic1.pk}) + private_url2 = reverse('epics-unwatch', kwargs={"pk": data.private_epic2.pk}) + blocked_url = reverse('epics-unwatch', kwargs={"pk": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + results = helper_test_http_method(client, 'post', blocked_url, "", users) + assert results == [404, 404, 404, 451, 451] + + +def test_epic_watchers_list(client, data): + public_url = reverse('epic-watchers-list', kwargs={"resource_id": data.public_epic.pk}) + private_url1 = reverse('epic-watchers-list', kwargs={"resource_id": data.private_epic1.pk}) + private_url2 = reverse('epic-watchers-list', kwargs={"resource_id": data.private_epic2.pk}) + blocked_url = reverse('epic-watchers-list', kwargs={"resource_id": data.blocked_epic.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_epic_watchers_retrieve(client, data): + add_watcher(data.public_epic, data.project_owner) + public_url = reverse('epic-watchers-detail', kwargs={"resource_id": data.public_epic.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_epic1, data.project_owner) + private_url1 = reverse('epic-watchers-detail', kwargs={"resource_id": data.private_epic1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_epic2, data.project_owner) + private_url2 = reverse('epic-watchers-detail', kwargs={"resource_id": data.private_epic2.pk, + "pk": data.project_owner.pk}) + + add_watcher(data.blocked_epic, data.project_owner) + blocked_url = reverse('epic-watchers-detail', kwargs={"resource_id": data.blocked_epic.pk, + "pk": data.project_owner.pk}) + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + results = helper_test_http_method(client, 'get', blocked_url, None, users) + assert results == [401, 403, 403, 200, 200] + + +#def test_epics_csv(client, data): +# url = reverse('epics-csv') +# csv_public_uuid = data.public_project.epics_csv_uuid +# csv_private1_uuid = data.private_project1.epics_csv_uuid +# csv_private2_uuid = data.private_project1.epics_csv_uuid +# csv_blocked_uuid = data.blocked_project.epics_csv_uuid +# +# users = [ +# None, +# data.registered_user, +# data.project_member_without_perms, +# data.project_member_with_perms, +# data.project_owner +# ] +# +# results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_public_uuid), None, users) +# assert results == [200, 200, 200, 200, 200] +# +# results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private1_uuid), None, users) +# assert results == [200, 200, 200, 200, 200] +# +# results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) +# assert results == [200, 200, 200, 200, 200] +# +# results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_blocked_uuid), None, users) +# assert results == [200, 200, 200, 200, 200]