diff --git a/dumpdata_role.sh b/dumpdata_role.sh
deleted file mode 100755
index 594a5d1d..00000000
--- a/dumpdata_role.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/bash
-
-python ./manage.py dumpdata -n --indent=4 users.Role > taiga/users/fixtures/initial_role.json
diff --git a/regenerate.sh b/regenerate.sh
index 4592a94e..c7761833 100755
--- a/regenerate.sh
+++ b/regenerate.sh
@@ -15,10 +15,11 @@ echo "-> Load initial domain"
python manage.py loaddata initial_domains --traceback
echo "-> Load initial user"
python manage.py loaddata initial_user --traceback
+echo "-> Load initial project_templates"
+python manage.py loaddata initial_project_templates --traceback
echo "-> Load initial roles"
python manage.py loaddata initial_role --traceback
echo "-> Generate sample data"
python manage.py sample_data --traceback
echo "-> Generate initial versions of objects"
python manage.py createinitialrevisions --traceback
-
diff --git a/settings/common.py b/settings/common.py
index 19527515..8a301574 100644
--- a/settings/common.py
+++ b/settings/common.py
@@ -293,6 +293,7 @@ REST_FRAMEWORK = {
"MAX_PAGINATE_BY": 1000,
}
+DEFAULT_PROJECT_TEMPLATE = "scrum"
# NOTE: DON'T INSERT MORE SETTINGS AFTER THIS LINE
diff --git a/taiga/auth/tests.py b/taiga/auth/tests.py
index 529ae4d1..e7e8bb52 100644
--- a/taiga/auth/tests.py
+++ b/taiga/auth/tests.py
@@ -56,7 +56,7 @@ urls.urlpatterns += patterns("",
class AuthServicesTests(test.TestCase):
- fixtures = ["initial_domains.json",]
+ fixtures = ["initial_domains.json", "initial_project_templates.json",]
def setUp(self):
self.user1 = create_user(1)
@@ -184,7 +184,7 @@ class TokenAuthenticationBackendTests(test.TestCase):
class RegisterApiTests(test.TestCase):
- fixtures = ["initial_domains.json",]
+ fixtures = ["initial_domains.json", "initial_project_templates.json",]
def setUp(self):
self.user1 = create_user(1)
diff --git a/taiga/base/serializers.py b/taiga/base/serializers.py
index d5d8dd17..eb5b3580 100644
--- a/taiga/base/serializers.py
+++ b/taiga/base/serializers.py
@@ -21,6 +21,8 @@ from rest_framework import serializers
from reversion.models import Version
import reversion
+from taiga.domains.base import get_active_domain
+
class PickleField(serializers.WritableField):
"""
@@ -33,6 +35,27 @@ class PickleField(serializers.WritableField):
return data
+class JsonField(serializers.WritableField):
+ """
+ Json objects serializer.
+ """
+ def to_native(self, obj):
+ return obj
+
+ def from_native(self, data):
+ return data
+
+class AutoDomainField(serializers.WritableField):
+ """
+ Automatically set domain field serializer.
+ """
+ def to_native(self, obj):
+ return obj
+
+ def from_native(self, data):
+ domain = get_active_domain()
+ return domain.id
+
class VersionSerializer(serializers.ModelSerializer):
created_date = serializers.SerializerMethodField("get_created_date")
content_type = serializers.SerializerMethodField("get_content_type")
diff --git a/taiga/domains/serializers.py b/taiga/domains/serializers.py
index 1537aa04..3f0ccb26 100644
--- a/taiga/domains/serializers.py
+++ b/taiga/domains/serializers.py
@@ -12,6 +12,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+from django.conf import settings
+
from rest_framework import serializers
from taiga.users.serializers import UserSerializer
@@ -20,14 +22,18 @@ from .models import Domain, DomainMember
class DomainSerializer(serializers.ModelSerializer):
projects = serializers.SerializerMethodField('get_projects')
+ default_project_template = serializers.SerializerMethodField('get_default_project_template')
class Meta:
model = Domain
- fields = ('public_register', 'default_language', "projects")
+ fields = ('public_register', 'default_language', "projects", "default_project_template")
def get_projects(self, obj):
return map(lambda x: {"id": x.id, "name": x.name, "slug": x.slug, "owner": x.owner.id}, obj.projects.all().order_by('name'))
+ def get_default_project_template(self, obj):
+ return settings.DEFAULT_PROJECT_TEMPLATE
+
class DomainMemberSerializer(serializers.ModelSerializer):
user = UserSerializer()
diff --git a/taiga/events/tests.py b/taiga/events/tests.py
index b8c02be1..ea714a65 100644
--- a/taiga/events/tests.py
+++ b/taiga/events/tests.py
@@ -51,7 +51,7 @@ from unittest.mock import MagicMock
from unittest.mock import patch
class ChangesTest(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def test_emit_change_for_model(self):
user = create_user(1) # Project owner
diff --git a/taiga/projects/api.py b/taiga/projects/api.py
index 58d52b93..c079a49f 100644
--- a/taiga/projects/api.py
+++ b/taiga/projects/api.py
@@ -298,3 +298,12 @@ class QuestionStatusViewSet(ModelCrudViewSet, BulkUpdateOrderMixin):
bulk_update_param = "bulk_question_statuses"
bulk_update_perm = "change_questionstatus"
bulk_update_order = services.bulk_update_question_status_order
+
+class ProjectTemplateViewSet(ModelCrudViewSet):
+ model = models.ProjectTemplate
+ serializer_class = serializers.ProjectTemplateSerializer
+ permission_classes = (IsAuthenticated, permissions.ProjectTemplatePermission)
+
+ def get_queryset(self):
+ domain = get_active_domain()
+ return models.ProjectTemplate.objects.filter(Q(domain=domain) | Q(domain__isnull=True))
diff --git a/taiga/projects/choices.py b/taiga/projects/choices.py
index 3a67a4f2..d1c9171b 100644
--- a/taiga/projects/choices.py
+++ b/taiga/projects/choices.py
@@ -14,463 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
-from django.utils.translation import ugettext_lazy as _
-
-
VIDEOCONFERENCES_CHOICES = (
("appear-in", "AppearIn"),
("talky", "Talky"),
)
-
-US_STATUSES = (
- (1, _("Open"), False, True, "#669933"),
- (2, _("Closed"), True, False, "#999999"),
-)
-
-TASK_STATUSES = (
- (1, _("New"), False, True, "#999999"),
- (2, _("In progress"), False, False, "#ff9900"),
- (3, _("Ready for test"), True, False, "#ffcc00"),
- (4, _("Closed"), True, False, "#669900"),
- (5, _("Needs Info"), False, False, "#999999"),
-)
-
-POINTS_CHOICES = (
- (1, u"?", None, True),
- (2, u"0", 0, False),
- (3, u"1/2", 0.5, False),
- (4, u"1", 1, False),
- (5, u"2", 2, False),
- (6, u"3", 3, False),
- (7, u"5", 5, False),
- (8, u"8", 8, False),
- (9, u"10", 10, False),
- (10, u"15", 15, False),
- (11, u"20", 20, False),
- (12, u"40", 40, False),
-)
-
-PRIORITY_CHOICES = (
- (1, _(u"Low"), "#666666", False),
- (3, _(u"Normal"), "#669933", True),
- (5, _(u"High"), "#CC0000", False),
-)
-
-SEVERITY_CHOICES = (
- (1, _(u"Wishlist"), "#666666", False),
- (2, _(u"Minor"), "#669933", False),
- (3, _(u"Normal"), "#0000FF", True),
- (4, _(u"Important"), "#FFA500", False),
- (5, _(u"Critical"), "#CC0000", False),
-)
-
-ISSUE_STATUSES = (
- (1, _("New"), False, "#8C2318", True),
- (2, _("In progress"), False, "#5E8C6A", False),
- (3, _("Ready for test"), True, "#88A65E", False),
- (4, _("Closed"), True, "#BFB35A", False),
- (5, _("Needs Info"), False, "#89BAB4", False),
- (6, _("Rejected"), True, "#CC0000", False),
- (7, _("Postponed"), False, "#666666", False),
-)
-
-ISSUE_TYPES = (
- (1, _(u"Bug"), "#89BAB4", True),
-)
-
-QUESTION_STATUS = (
- (1, _("Pending"), False, "#FFA500", True),
- (2, _("Answered"), False, "#669933", False),
- (3, _("Closed"), True,"#BFB35A", False),
-)
-
-ROLES = (
- (10, "ux", "UX", True, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "add_milestone", "milestones", "milestone" ],
- [ "change_milestone", "milestones", "milestone" ],
- [ "delete_milestone", "milestones", "milestone" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "add_issuestatus", "projects", "issuestatus" ],
- [ "change_issuestatus", "projects", "issuestatus" ],
- [ "delete_issuestatus", "projects", "issuestatus" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "add_issuetype", "projects", "issuetype" ],
- [ "change_issuetype", "projects", "issuetype" ],
- [ "delete_issuetype", "projects", "issuetype" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "add_membership", "projects", "membership" ],
- [ "change_membership", "projects", "membership" ],
- [ "delete_membership", "projects", "membership" ],
- [ "view_membership", "projects", "membership" ],
- [ "add_points", "projects", "points" ],
- [ "change_points", "projects", "points" ],
- [ "delete_points", "projects", "points" ],
- [ "view_points", "projects", "points" ],
- [ "add_priority", "projects", "priority" ],
- [ "change_priority", "projects", "priority" ],
- [ "delete_priority", "projects", "priority" ],
- [ "view_priority", "projects", "priority" ],
- [ "add_project", "projects", "project" ],
- [ "change_project", "projects", "project" ],
- [ "delete_project", "projects", "project" ],
- [ "view_project", "projects", "project" ],
- [ "add_severity", "projects", "severity" ],
- [ "change_severity", "projects", "severity" ],
- [ "delete_severity", "projects", "severity" ],
- [ "view_severity", "projects", "severity" ],
- [ "add_taskstatus", "projects", "taskstatus" ],
- [ "change_taskstatus", "projects", "taskstatus" ],
- [ "delete_taskstatus", "projects", "taskstatus" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "add_userstorystatus", "projects", "userstorystatus" ],
- [ "change_userstorystatus", "projects", "userstorystatus" ],
- [ "delete_userstorystatus", "projects", "userstorystatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "add_task", "tasks", "task" ],
- [ "change_task", "tasks", "task" ],
- [ "delete_task", "tasks", "task" ],
- [ "view_task", "tasks", "task" ],
- [ "add_role", "users", "role" ],
- [ "change_role", "users", "role" ],
- [ "delete_role", "users", "role" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "add_rolepoints", "userstories", "rolepoints" ],
- [ "change_rolepoints", "userstories", "rolepoints" ],
- [ "delete_rolepoints", "userstories", "rolepoints" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "add_userstory", "userstories", "userstory" ],
- [ "change_userstory", "userstories", "userstory" ],
- [ "delete_userstory", "userstories", "userstory" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "add_wikipage", "wiki", "wikipage" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "delete_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ]),
- (20, "design", "Design", True, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "add_milestone", "milestones", "milestone" ],
- [ "change_milestone", "milestones", "milestone" ],
- [ "delete_milestone", "milestones", "milestone" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "add_issuestatus", "projects", "issuestatus" ],
- [ "change_issuestatus", "projects", "issuestatus" ],
- [ "delete_issuestatus", "projects", "issuestatus" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "add_issuetype", "projects", "issuetype" ],
- [ "change_issuetype", "projects", "issuetype" ],
- [ "delete_issuetype", "projects", "issuetype" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "add_membership", "projects", "membership" ],
- [ "change_membership", "projects", "membership" ],
- [ "delete_membership", "projects", "membership" ],
- [ "view_membership", "projects", "membership" ],
- [ "add_points", "projects", "points" ],
- [ "change_points", "projects", "points" ],
- [ "delete_points", "projects", "points" ],
- [ "view_points", "projects", "points" ],
- [ "add_priority", "projects", "priority" ],
- [ "change_priority", "projects", "priority" ],
- [ "delete_priority", "projects", "priority" ],
- [ "view_priority", "projects", "priority" ],
- [ "add_project", "projects", "project" ],
- [ "change_project", "projects", "project" ],
- [ "delete_project", "projects", "project" ],
- [ "view_project", "projects", "project" ],
- [ "add_severity", "projects", "severity" ],
- [ "change_severity", "projects", "severity" ],
- [ "delete_severity", "projects", "severity" ],
- [ "view_severity", "projects", "severity" ],
- [ "add_taskstatus", "projects", "taskstatus" ],
- [ "change_taskstatus", "projects", "taskstatus" ],
- [ "delete_taskstatus", "projects", "taskstatus" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "add_userstorystatus", "projects", "userstorystatus" ],
- [ "change_userstorystatus", "projects", "userstorystatus" ],
- [ "delete_userstorystatus", "projects", "userstorystatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "add_task", "tasks", "task" ],
- [ "change_task", "tasks", "task" ],
- [ "delete_task", "tasks", "task" ],
- [ "view_task", "tasks", "task" ],
- [ "add_role", "users", "role" ],
- [ "change_role", "users", "role" ],
- [ "delete_role", "users", "role" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "add_rolepoints", "userstories", "rolepoints" ],
- [ "change_rolepoints", "userstories", "rolepoints" ],
- [ "delete_rolepoints", "userstories", "rolepoints" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "add_userstory", "userstories", "userstory" ],
- [ "change_userstory", "userstories", "userstory" ],
- [ "delete_userstory", "userstories", "userstory" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "add_wikipage", "wiki", "wikipage" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "delete_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ]),
- (30, "front", "Front", True, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "add_milestone", "milestones", "milestone" ],
- [ "change_milestone", "milestones", "milestone" ],
- [ "delete_milestone", "milestones", "milestone" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "add_issuestatus", "projects", "issuestatus" ],
- [ "change_issuestatus", "projects", "issuestatus" ],
- [ "delete_issuestatus", "projects", "issuestatus" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "add_issuetype", "projects", "issuetype" ],
- [ "change_issuetype", "projects", "issuetype" ],
- [ "delete_issuetype", "projects", "issuetype" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "add_membership", "projects", "membership" ],
- [ "change_membership", "projects", "membership" ],
- [ "delete_membership", "projects", "membership" ],
- [ "view_membership", "projects", "membership" ],
- [ "add_points", "projects", "points" ],
- [ "change_points", "projects", "points" ],
- [ "delete_points", "projects", "points" ],
- [ "view_points", "projects", "points" ],
- [ "add_priority", "projects", "priority" ],
- [ "change_priority", "projects", "priority" ],
- [ "delete_priority", "projects", "priority" ],
- [ "view_priority", "projects", "priority" ],
- [ "add_project", "projects", "project" ],
- [ "change_project", "projects", "project" ],
- [ "delete_project", "projects", "project" ],
- [ "view_project", "projects", "project" ],
- [ "add_severity", "projects", "severity" ],
- [ "change_severity", "projects", "severity" ],
- [ "delete_severity", "projects", "severity" ],
- [ "view_severity", "projects", "severity" ],
- [ "add_taskstatus", "projects", "taskstatus" ],
- [ "change_taskstatus", "projects", "taskstatus" ],
- [ "delete_taskstatus", "projects", "taskstatus" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "add_userstorystatus", "projects", "userstorystatus" ],
- [ "change_userstorystatus", "projects", "userstorystatus" ],
- [ "delete_userstorystatus", "projects", "userstorystatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "add_task", "tasks", "task" ],
- [ "change_task", "tasks", "task" ],
- [ "delete_task", "tasks", "task" ],
- [ "view_task", "tasks", "task" ],
- [ "add_role", "users", "role" ],
- [ "change_role", "users", "role" ],
- [ "delete_role", "users", "role" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "add_rolepoints", "userstories", "rolepoints" ],
- [ "change_rolepoints", "userstories", "rolepoints" ],
- [ "delete_rolepoints", "userstories", "rolepoints" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "add_userstory", "userstories", "userstory" ],
- [ "change_userstory", "userstories", "userstory" ],
- [ "delete_userstory", "userstories", "userstory" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "add_wikipage", "wiki", "wikipage" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "delete_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ]),
- (40, "back", "Back", True, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "add_milestone", "milestones", "milestone" ],
- [ "change_milestone", "milestones", "milestone" ],
- [ "delete_milestone", "milestones", "milestone" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "add_issuestatus", "projects", "issuestatus" ],
- [ "change_issuestatus", "projects", "issuestatus" ],
- [ "delete_issuestatus", "projects", "issuestatus" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "add_issuetype", "projects", "issuetype" ],
- [ "change_issuetype", "projects", "issuetype" ],
- [ "delete_issuetype", "projects", "issuetype" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "add_membership", "projects", "membership" ],
- [ "change_membership", "projects", "membership" ],
- [ "delete_membership", "projects", "membership" ],
- [ "view_membership", "projects", "membership" ],
- [ "add_points", "projects", "points" ],
- [ "change_points", "projects", "points" ],
- [ "delete_points", "projects", "points" ],
- [ "view_points", "projects", "points" ],
- [ "add_priority", "projects", "priority" ],
- [ "change_priority", "projects", "priority" ],
- [ "delete_priority", "projects", "priority" ],
- [ "view_priority", "projects", "priority" ],
- [ "add_project", "projects", "project" ],
- [ "change_project", "projects", "project" ],
- [ "delete_project", "projects", "project" ],
- [ "view_project", "projects", "project" ],
- [ "add_severity", "projects", "severity" ],
- [ "change_severity", "projects", "severity" ],
- [ "delete_severity", "projects", "severity" ],
- [ "view_severity", "projects", "severity" ],
- [ "add_taskstatus", "projects", "taskstatus" ],
- [ "change_taskstatus", "projects", "taskstatus" ],
- [ "delete_taskstatus", "projects", "taskstatus" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "add_userstorystatus", "projects", "userstorystatus" ],
- [ "change_userstorystatus", "projects", "userstorystatus" ],
- [ "delete_userstorystatus", "projects", "userstorystatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "add_task", "tasks", "task" ],
- [ "change_task", "tasks", "task" ],
- [ "delete_task", "tasks", "task" ],
- [ "view_task", "tasks", "task" ],
- [ "add_role", "users", "role" ],
- [ "change_role", "users", "role" ],
- [ "delete_role", "users", "role" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "add_rolepoints", "userstories", "rolepoints" ],
- [ "change_rolepoints", "userstories", "rolepoints" ],
- [ "delete_rolepoints", "userstories", "rolepoints" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "add_userstory", "userstories", "userstory" ],
- [ "change_userstory", "userstories", "userstory" ],
- [ "delete_userstory", "userstories", "userstory" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "add_wikipage", "wiki", "wikipage" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "delete_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ]),
- (50, "product-ouner", "Product Owner", False, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "add_milestone", "milestones", "milestone" ],
- [ "change_milestone", "milestones", "milestone" ],
- [ "delete_milestone", "milestones", "milestone" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "add_issuestatus", "projects", "issuestatus" ],
- [ "change_issuestatus", "projects", "issuestatus" ],
- [ "delete_issuestatus", "projects", "issuestatus" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "add_issuetype", "projects", "issuetype" ],
- [ "change_issuetype", "projects", "issuetype" ],
- [ "delete_issuetype", "projects", "issuetype" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "add_membership", "projects", "membership" ],
- [ "change_membership", "projects", "membership" ],
- [ "delete_membership", "projects", "membership" ],
- [ "view_membership", "projects", "membership" ],
- [ "add_points", "projects", "points" ],
- [ "change_points", "projects", "points" ],
- [ "delete_points", "projects", "points" ],
- [ "view_points", "projects", "points" ],
- [ "add_priority", "projects", "priority" ],
- [ "change_priority", "projects", "priority" ],
- [ "delete_priority", "projects", "priority" ],
- [ "view_priority", "projects", "priority" ],
- [ "add_project", "projects", "project" ],
- [ "change_project", "projects", "project" ],
- [ "delete_project", "projects", "project" ],
- [ "view_project", "projects", "project" ],
- [ "add_severity", "projects", "severity" ],
- [ "change_severity", "projects", "severity" ],
- [ "delete_severity", "projects", "severity" ],
- [ "view_severity", "projects", "severity" ],
- [ "add_taskstatus", "projects", "taskstatus" ],
- [ "change_taskstatus", "projects", "taskstatus" ],
- [ "delete_taskstatus", "projects", "taskstatus" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "add_userstorystatus", "projects", "userstorystatus" ],
- [ "change_userstorystatus", "projects", "userstorystatus" ],
- [ "delete_userstorystatus", "projects", "userstorystatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "add_task", "tasks", "task" ],
- [ "change_task", "tasks", "task" ],
- [ "delete_task", "tasks", "task" ],
- [ "view_task", "tasks", "task" ],
- [ "add_role", "users", "role" ],
- [ "change_role", "users", "role" ],
- [ "delete_role", "users", "role" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "add_rolepoints", "userstories", "rolepoints" ],
- [ "change_rolepoints", "userstories", "rolepoints" ],
- [ "delete_rolepoints", "userstories", "rolepoints" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "add_userstory", "userstories", "userstory" ],
- [ "change_userstory", "userstories", "userstory" ],
- [ "delete_userstory", "userstories", "userstory" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "add_wikipage", "wiki", "wikipage" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "delete_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ]),
- (60, "stakeholder", "Stakeholder", False, [
- [ "add_issue", "issues", "issue" ],
- [ "change_issue", "issues", "issue" ],
- [ "delete_issue", "issues", "issue" ],
- [ "view_issue", "issues", "issue" ],
- [ "view_milestone", "milestones", "milestone" ],
- [ "add_attachment", "projects", "attachment" ],
- [ "change_attachment", "projects", "attachment" ],
- [ "delete_attachment", "projects", "attachment" ],
- [ "view_attachment", "projects", "attachment" ],
- [ "view_issuestatus", "projects", "issuestatus" ],
- [ "view_issuetype", "projects", "issuetype" ],
- [ "view_membership", "projects", "membership" ],
- [ "view_points", "projects", "points" ],
- [ "view_priority", "projects", "priority" ],
- [ "view_project", "projects", "project" ],
- [ "view_severity", "projects", "severity" ],
- [ "view_taskstatus", "projects", "taskstatus" ],
- [ "view_userstorystatus", "projects", "userstorystatus" ],
- [ "view_task", "tasks", "task" ],
- [ "view_role", "users", "role" ],
- [ "change_user", "users", "user" ],
- [ "view_user", "users", "user" ],
- [ "view_rolepoints", "userstories", "rolepoints" ],
- [ "view_userstory", "userstories", "userstory" ],
- [ "change_wikipage", "wiki", "wikipage" ],
- [ "view_wikipage", "wiki", "wikipage" ]
- ])
-)
diff --git a/taiga/projects/fixtures/initial_project_templates.json b/taiga/projects/fixtures/initial_project_templates.json
new file mode 100644
index 00000000..1a306ff7
--- /dev/null
+++ b/taiga/projects/fixtures/initial_project_templates.json
@@ -0,0 +1,56 @@
+[
+{
+ "model": "projects.projecttemplate",
+ "pk": 1,
+ "fields": {
+ "modified_date": "2014-04-22T14:48:43.596Z",
+ "roles": "[{\"order\": 10, \"slug\": \"ux\", \"name\": \"UX\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"computable\": true}, {\"order\": 20, \"slug\": \"design\", \"name\": \"Design\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"computable\": true}, {\"order\": 30, \"slug\": \"front\", \"name\": \"Front\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"computable\": true}, {\"order\": 40, \"slug\": \"back\", \"name\": \"Back\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"computable\": true}, {\"order\": 50, \"slug\": \"product-ouner\", \"name\": \"Product Owner\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"computable\": false}, {\"order\": 60, \"slug\": \"stakeholder\", \"name\": \"Stakeholder\", \"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"view_issuestatus\", \"view_issuetype\", \"view_membership\", \"view_points\", \"view_priority\", \"view_project\", \"view_severity\", \"view_taskstatus\", \"view_userstorystatus\", \"view_task\", \"view_role\", \"change_user\", \"view_user\", \"view_rolepoints\", \"view_userstory\", \"change_wikipage\", \"view_wikipage\"], \"computable\": false}]",
+ "is_kanban_activated": false,
+ "is_wiki_activated": 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\": \"15\", \"value\": 15.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]",
+ "videoconferences": null,
+ "name": "Scrum",
+ "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#89BAB4\"}]",
+ "is_issues_activated": true,
+ "description": "",
+ "created_date": "2014-04-22T14:48:43.596Z",
+ "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#8C2318\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#5E8C6A\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#88A65E\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#BFB35A\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#89BAB4\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#CC0000\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#666666\", \"is_closed\": false}]",
+ "slug": "scrum",
+ "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#ff9900\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#ffcc00\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#669900\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#999999\", \"is_closed\": false}]",
+ "default_options": "{\"us_status\": \"Open\", \"priority\": \"Normal\", \"issue_status\": \"New\", \"issue_type\": \"Bug\", \"points\": \"?\", \"severity\": \"Normal\", \"task_status\": \"New\"}",
+ "is_backlog_activated": true,
+ "us_statuses": "[{\"order\": 1, \"name\": \"Open\", \"color\": \"#669933\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Closed\", \"color\": \"#999999\", \"is_closed\": true, \"wip_limit\": null}]",
+ "videoconferences_salt": null,
+ "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\"}]",
+ "domain": null
+ }
+},
+{
+ "model": "projects.projecttemplate",
+ "pk": 2,
+ "fields": {
+ "modified_date": "2014-04-22T14:50:19.738Z",
+ "roles": "[{\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"slug\": \"ux\", \"computable\": true, \"order\": 10, \"name\": \"UX\"}, {\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"slug\": \"design\", \"computable\": true, \"order\": 20, \"name\": \"Design\"}, {\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"slug\": \"front\", \"computable\": true, \"order\": 30, \"name\": \"Front\"}, {\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"slug\": \"back\", \"computable\": true, \"order\": 40, \"name\": \"Back\"}, {\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"add_milestone\", \"change_milestone\", \"delete_milestone\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"add_issuestatus\", \"change_issuestatus\", \"delete_issuestatus\", \"view_issuestatus\", \"add_issuetype\", \"change_issuetype\", \"delete_issuetype\", \"view_issuetype\", \"add_membership\", \"change_membership\", \"delete_membership\", \"view_membership\", \"add_points\", \"change_points\", \"delete_points\", \"view_points\", \"add_priority\", \"change_priority\", \"delete_priority\", \"view_priority\", \"add_project\", \"change_project\", \"delete_project\", \"view_project\", \"add_severity\", \"change_severity\", \"delete_severity\", \"view_severity\", \"add_taskstatus\", \"change_taskstatus\", \"delete_taskstatus\", \"view_taskstatus\", \"add_userstorystatus\", \"change_userstorystatus\", \"delete_userstorystatus\", \"view_userstorystatus\", \"add_task\", \"change_task\", \"delete_task\", \"view_task\", \"add_role\", \"change_role\", \"delete_role\", \"view_role\", \"change_user\", \"view_user\", \"add_rolepoints\", \"change_rolepoints\", \"delete_rolepoints\", \"view_rolepoints\", \"add_userstory\", \"change_userstory\", \"delete_userstory\", \"view_userstory\", \"add_wikipage\", \"change_wikipage\", \"delete_wikipage\", \"view_wikipage\"], \"slug\": \"product-ouner\", \"computable\": false, \"order\": 50, \"name\": \"Product Owner\"}, {\"permissions\": [\"add_issue\", \"change_issue\", \"delete_issue\", \"view_issue\", \"view_milestone\", \"add_attachment\", \"change_attachment\", \"delete_attachment\", \"view_attachment\", \"view_issuestatus\", \"view_issuetype\", \"view_membership\", \"view_points\", \"view_priority\", \"view_project\", \"view_severity\", \"view_taskstatus\", \"view_userstorystatus\", \"view_task\", \"view_role\", \"change_user\", \"view_user\", \"view_rolepoints\", \"view_userstory\", \"change_wikipage\", \"view_wikipage\"], \"slug\": \"stakeholder\", \"computable\": false, \"order\": 60, \"name\": \"Stakeholder\"}]",
+ "is_kanban_activated": true,
+ "is_wiki_activated": false,
+ "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\": \"15\", \"value\": 15.0}, {\"order\": 11, \"name\": \"20\", \"value\": 20.0}, {\"order\": 12, \"name\": \"40\", \"value\": 40.0}]",
+ "videoconferences": null,
+ "name": "Kanban",
+ "issue_types": "[{\"order\": 1, \"name\": \"Bug\", \"color\": \"#89BAB4\"}]",
+ "is_issues_activated": false,
+ "description": "",
+ "created_date": "2014-04-22T14:50:19.738Z",
+ "issue_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#8C2318\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#5E8C6A\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#88A65E\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#BFB35A\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#89BAB4\", \"is_closed\": false}, {\"order\": 6, \"name\": \"Rejected\", \"color\": \"#CC0000\", \"is_closed\": true}, {\"order\": 7, \"name\": \"Postponed\", \"color\": \"#666666\", \"is_closed\": false}]",
+ "slug": "kanban",
+ "task_statuses": "[{\"order\": 1, \"name\": \"New\", \"color\": \"#999999\", \"is_closed\": false}, {\"order\": 2, \"name\": \"In progress\", \"color\": \"#ff9900\", \"is_closed\": false}, {\"order\": 3, \"name\": \"Ready for test\", \"color\": \"#ffcc00\", \"is_closed\": true}, {\"order\": 4, \"name\": \"Closed\", \"color\": \"#669900\", \"is_closed\": true}, {\"order\": 5, \"name\": \"Needs Info\", \"color\": \"#999999\", \"is_closed\": false}]",
+ "default_options": "{\"us_status\": \"To do\", \"priority\": \"Normal\", \"issue_status\": \"New\", \"issue_type\": \"Bug\", \"points\": \"?\", \"severity\": \"Normal\", \"task_status\": \"New\"}",
+ "is_backlog_activated": false,
+ "us_statuses": "[{\"order\": 1, \"name\": \"To do\", \"color\": \"#999999\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 2, \"name\": \"Doing\", \"color\": \"#ff9900\", \"is_closed\": false, \"wip_limit\": null}, {\"order\": 3, \"name\": \"Done\", \"color\": \"#ffcc00\", \"is_closed\": true, \"wip_limit\": null}]",
+ "videoconferences_salt": null,
+ "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\"}]",
+ "domain": null
+ }
+}
+]
diff --git a/taiga/projects/issues/tests/tests_api.py b/taiga/projects/issues/tests/tests_api.py
index b9b193f6..0ebfdc44 100644
--- a/taiga/projects/issues/tests/tests_api.py
+++ b/taiga/projects/issues/tests/tests_api.py
@@ -15,7 +15,7 @@ from . import create_issue
class IssuesTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1) # Project owner
diff --git a/taiga/projects/milestones/tests/tests_api.py b/taiga/projects/milestones/tests/tests_api.py
index f4277531..2197cce3 100644
--- a/taiga/projects/milestones/tests/tests_api.py
+++ b/taiga/projects/milestones/tests/tests_api.py
@@ -30,7 +30,7 @@ from . import create_milestone
class MilestonesTestCase(test.TestCase):
- fixtures = ["initial_domains.json",]
+ fixtures = ["initial_domains.json", "initial_project_templates.json",]
def setUp(self):
self.user1 = create_user(1)
diff --git a/taiga/projects/models.py b/taiga/projects/models.py
index 1ac45bd5..6553d412 100644
--- a/taiga/projects/models.py
+++ b/taiga/projects/models.py
@@ -31,6 +31,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from picklefield.fields import PickledObjectField
+from django_pgjson.fields import JsonField
from taiga.users.models import Role
from taiga.domains.models import DomainMember
@@ -520,6 +521,241 @@ class QuestionStatus(models.Model):
return self.name
+class ProjectTemplate(models.Model):
+ name = models.CharField(max_length=250, unique=True, null=False, blank=False,
+ verbose_name=_("name"))
+ slug = models.SlugField(max_length=250, unique=True, null=False, blank=True,
+ verbose_name=_("slug"))
+ description = models.TextField(null=False, blank=False,
+ verbose_name=_("description"))
+ created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
+ verbose_name=_("created date"))
+ modified_date = models.DateTimeField(auto_now=True, null=False, blank=False,
+ verbose_name=_("modified date"))
+ domain = models.ForeignKey("domains.Domain", related_name="templates", null=True, blank=True,
+ default=None, verbose_name=_("domain"))
+
+ is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
+ verbose_name=_("active backlog panel"))
+ is_kanban_activated = models.BooleanField(default=False, null=False, blank=True,
+ verbose_name=_("active kanban panel"))
+ is_wiki_activated = models.BooleanField(default=True, null=False, blank=True,
+ verbose_name=_("active wiki panel"))
+ is_issues_activated = models.BooleanField(default=True, null=False, blank=True,
+ verbose_name=_("active issues panel"))
+ videoconferences = models.CharField(max_length=250, null=True, blank=True,
+ choices=choices.VIDEOCONFERENCES_CHOICES,
+ verbose_name=_("videoconference system"))
+ videoconferences_salt = models.CharField(max_length=250, null=True, blank=True,
+ verbose_name=_("videoconference room salt"))
+
+ default_options = JsonField(null=True, blank=True, default=None, verbose_name=_("default options"))
+ us_statuses = JsonField(null=True, blank=True, default=None, verbose_name=_("us statuses"))
+ points = JsonField(null=True, blank=True, default=None, verbose_name=_("us points"))
+ task_statuses = JsonField(null=True, blank=True, default=None, verbose_name=_("task statuses"))
+ issue_statuses = JsonField(null=True, blank=True, default=None, verbose_name=_("issue statuses"))
+ issue_types = JsonField(null=True, blank=True, default=None, verbose_name=_("issue types"))
+ priorities = JsonField(null=True, blank=True, default=None, verbose_name=_("issue types"))
+ severities = JsonField(null=True, blank=True, default=None, verbose_name=_("issue types"))
+ roles = JsonField(null=True, blank=True, default=None, verbose_name=_("roles"))
+
+ class Meta:
+ verbose_name = "project template"
+ verbose_name_plural = "project templates"
+ ordering = ["name"]
+
+ def __str__(self):
+ return self.name
+
+ def __repr__(self):
+ return "".format(self.slug)
+
+ def save(self, *args, **kwargs):
+ super().save(*args, **kwargs)
+
+ def load_data_from_project(self, project):
+ self.is_backlog_activated = project.is_backlog_activated
+ self.is_kanban_activated = project.is_kanban_activated
+ self.is_wiki_activated = project.is_wiki_activated
+ self.is_issues_activated = project.is_issues_activated
+ self.videoconferences = project.videoconferences
+ self.videoconferences_salt = project.videoconferences_salt
+
+ self.default_options = {
+ "points": project.default_points.name,
+ "us_status": project.default_us_status.name,
+ "task_status": project.default_task_status.name,
+ "issue_status": project.default_issue_status.name,
+ "issue_type": project.default_issue_type.name,
+ "priority": project.default_priority.name,
+ "severity": project.default_severity.name
+ }
+
+
+ self.us_statuses = []
+ for us_status in project.us_statuses.all():
+ self.us_statuses.append({
+ "name": us_status.name,
+ "is_closed": us_status.is_closed,
+ "color": us_status.color,
+ "wip_limit": us_status.wip_limit,
+ "order": us_status.order,
+ })
+
+ self.points = []
+ for us_point in project.points.all():
+ self.points.append({
+ "name": us_point.name,
+ "value": us_point.value,
+ "order": us_point.order,
+ })
+
+ self.task_statuses = []
+ for task_status in project.task_statuses.all():
+ self.task_statuses.append({
+ "name": task_status.name,
+ "is_closed": task_status.is_closed,
+ "color": task_status.color,
+ "order": task_status.order,
+ })
+
+ self.issue_statuses = []
+ for issue_status in project.issue_statuses.all():
+ self.issue_statuses.append({
+ "name": issue_status.name,
+ "is_closed": issue_status.is_closed,
+ "color": issue_status.color,
+ "order": issue_status.order,
+ })
+
+ self.issue_types = []
+ for issue_type in project.issue_types.all():
+ self.issue_types.append({
+ "name": issue_type.name,
+ "color": issue_type.color,
+ "order": issue_type.order,
+ })
+
+ self.priorities = []
+ for priority in project.priorities.all():
+ self.priorities.append({
+ "name": priority.name,
+ "color": priority.color,
+ "order": priority.order,
+ })
+
+ self.severities = []
+ for severity in project.severities.all():
+ self.severities.append({
+ "name": severity.name,
+ "color": severity.color,
+ "order": severity.order,
+ })
+
+ self.roles = []
+ for role in project.roles.all():
+ permissions = [p.codename for p in role.permissions.all()]
+ self.roles.append({
+ "name": role.name,
+ "slug": role.slug,
+ "permissions": permissions,
+ "order": role.order,
+ "computable": role.computable
+ })
+
+ def apply_to_project(self, project):
+ if project.id is None:
+ raise "Project need an id (must be a saved project)"
+
+ project.is_backlog_activated = self.is_backlog_activated
+ project.is_kanban_activated = self.is_kanban_activated
+ project.is_wiki_activated = self.is_wiki_activated
+ project.is_issues_activated = self.is_issues_activated
+ project.videoconferences = self.videoconferences
+ project.videoconferences_salt = self.videoconferences_salt
+
+ for us_status in self.us_statuses:
+ UserStoryStatus.objects.create(
+ name=us_status["name"],
+ is_closed=us_status["is_closed"],
+ color=us_status["color"],
+ wip_limit=us_status["wip_limit"],
+ order=us_status["order"],
+ project=project
+ )
+
+ for point in self.points:
+ Points.objects.create(
+ name=point["name"],
+ value=point["value"],
+ order=point["order"],
+ project=project
+ )
+
+ for task_status in self.task_statuses:
+ TaskStatus.objects.create(
+ name=task_status["name"],
+ is_closed=task_status["is_closed"],
+ color=task_status["color"],
+ order=task_status["order"],
+ project=project
+ )
+
+ for issue_status in self.issue_statuses:
+ IssueStatus.objects.create(
+ name=issue_status["name"],
+ is_closed=issue_status["is_closed"],
+ color=issue_status["color"],
+ order=issue_status["order"],
+ project=project
+ )
+
+ for issue_type in self.issue_types:
+ IssueType.objects.create(
+ name=issue_type["name"],
+ color=issue_type["color"],
+ order=issue_type["order"],
+ project=project
+ )
+
+ for priority in self.priorities:
+ Priority.objects.create(
+ name=priority["name"],
+ color=priority["color"],
+ order=priority["order"],
+ project=project
+ )
+
+ for severity in self.severities:
+ Severity.objects.create(
+ name=severity["name"],
+ color=severity["color"],
+ order=severity["order"],
+ project=project
+ )
+
+ for role in self.roles:
+ newRoleInstance = Role.objects.create(
+ name=role["name"],
+ slug=role["slug"],
+ order=role["order"],
+ computable=role["computable"],
+ project=project
+ )
+ permissions = [Permission.objects.get(codename=codename) for codename in role["permissions"]]
+ for permission in permissions:
+ newRoleInstance.permissions.add(permission)
+
+ project.default_points = Points.objects.get(name=self.default_options["points"], project=project)
+ project.default_us_status = UserStoryStatus.objects.get(name=self.default_options["us_status"], project=project)
+ project.default_task_status = TaskStatus.objects.get(name=self.default_options["task_status"], project=project)
+ project.default_issue_status = IssueStatus.objects.get(name=self.default_options["issue_status"], project=project)
+ project.default_issue_type = IssueType.objects.get(name=self.default_options["issue_type"], project=project)
+ project.default_priority = Priority.objects.get(name=self.default_options["priority"], project=project)
+ project.default_severity = Severity.objects.get(name=self.default_options["severity"], project=project)
+
+ return project
+
# Reversion registration (usufull for base.notification and for meke a historical)
reversion.register(Project)
reversion.register(Attachment)
@@ -570,67 +806,7 @@ def project_post_save(sender, instance, created, **kwargs):
if not created:
return
- # USs
- for order, name, value, is_default in choices.POINTS_CHOICES:
- obj = Points.objects.create(project=instance, name=name, order=order, value=value)
- if is_default:
- instance.default_points = obj
-
- for order, name, is_closed, is_default, color in choices.US_STATUSES:
- obj = UserStoryStatus.objects.create(name=name, order=order, color=color,
- is_closed=is_closed, project=instance)
- if is_default:
- instance.default_us_status = obj
-
- # Tasks
- for order, name, is_closed, is_default, color in choices.TASK_STATUSES:
- obj = TaskStatus.objects.create(name=name, order=order, color=color,
- is_closed=is_closed, project=instance)
- if is_default:
- instance.default_task_status = obj
-
- # Issues
- for order, name, color, is_default in choices.PRIORITY_CHOICES:
- obj = Priority.objects.create(project=instance, name=name, order=order, color=color)
- if is_default:
- instance.default_priority = obj
-
- for order, name, color, is_default in choices.SEVERITY_CHOICES:
- obj = Severity.objects.create(project=instance, name=name, order=order, color=color)
- if is_default:
- instance.default_severity = obj
-
- for order, name, is_closed, color, is_default in choices.ISSUE_STATUSES:
- obj = IssueStatus.objects.create(name=name, order=order,
- is_closed=is_closed, project=instance, color=color)
- if is_default:
- instance.default_issue_status = obj
-
- for order, name, color, is_default in choices.ISSUE_TYPES:
- obj = IssueType.objects.create(project=instance, name=name, order=order, color=color)
- if is_default:
- instance.default_issue_type = obj
-
- # Questions
- for order, name, is_closed, color, is_default in choices.QUESTION_STATUS:
- obj = QuestionStatus.objects.create(name=name, order=order,
- is_closed=is_closed, project=instance, color=color)
- if is_default:
- instance.default_question_status = obj
-
- # Permissions
- for order, slug, name, computable, permissions in choices.ROLES:
- obj = Role.objects.create(slug=slug, name=name, order=order, computable=computable, project=instance)
- for permission in permissions:
- try:
- perm = Permission.objects.get(codename=permission[0], content_type__app_label=permission[1], content_type__model=permission[2])
- obj.permissions.add(perm)
- except Permission.DoesNotExist:
- pass
-
+ template_slug = getattr(instance, "template", settings.DEFAULT_PROJECT_TEMPLATE)
+ template = ProjectTemplate.objects.get(slug=template_slug, domain__isnull=True)
+ template.apply_to_project(instance)
instance.save()
-
- from taiga.projects.template_manager import ProjectTemplateManager
- if hasattr(instance, "template"):
- template_manager = ProjectTemplateManager()
- template_manager.apply(instance.template, instance)
diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py
index 0e1f9a0a..12ed9455 100644
--- a/taiga/projects/permissions.py
+++ b/taiga/projects/permissions.py
@@ -168,3 +168,12 @@ class QuestionStatusPermission(BasePermission):
delete_permission = "delete_questionstatus"
safe_methods = ["HEAD", "OPTIONS"]
path_to_project = ["project"]
+
+class ProjectTemplatePermission(BasePermission):
+ def has_permission(self, request, view):
+ domain = get_active_domain()
+ return domain.user_is_owner(request.user)
+
+ def has_object_permission(self, request, view, obj):
+ domain = get_active_domain()
+ return domain.user_is_owner(request.user)
diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py
index 749b466d..c465d55c 100644
--- a/taiga/projects/serializers.py
+++ b/taiga/projects/serializers.py
@@ -17,7 +17,7 @@
from os import path
from rest_framework import serializers
-from taiga.base.serializers import PickleField
+from taiga.base.serializers import PickleField, JsonField, AutoDomainField
from taiga.users.models import Role
from . import models
@@ -182,3 +182,20 @@ class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = ('id', 'name', 'permissions', 'computable', 'project', 'order')
+
+
+class ProjectTemplateSerializer(serializers.ModelSerializer):
+ domain = AutoDomainField()
+
+ default_options = JsonField()
+ us_statuses = JsonField()
+ points = JsonField()
+ task_statuses = JsonField()
+ issue_statuses = JsonField()
+ issue_types = JsonField()
+ priorities = JsonField()
+ severities = JsonField()
+ roles = JsonField()
+
+ class Meta:
+ model = models.ProjectTemplate
diff --git a/taiga/projects/tasks/tests/tests_api.py b/taiga/projects/tasks/tests/tests_api.py
index 5f12f495..feb2f582 100644
--- a/taiga/projects/tasks/tests/tests_api.py
+++ b/taiga/projects/tasks/tests/tests_api.py
@@ -34,7 +34,7 @@ from . import create_task
class TasksTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1) # Project owner
diff --git a/taiga/projects/template_manager.py b/taiga/projects/template_manager.py
deleted file mode 100644
index 08d8b77c..00000000
--- a/taiga/projects/template_manager.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2014 Andrey Antukh
-# Copyright (C) 2014 Jesús Espino
-# Copyright (C) 2014 David Barragán
-# 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 .
-
-from taiga.projects.models import UserStoryStatus
-
-
-class ProjectTemplateManager(object):
- def apply(self, template, project):
- if not hasattr(self, "template"):
- return False
- template = getattr(self, "template")
- template(project)
-
- def legal(self, project):
- pass
-
- def pure_kanban(self, project):
- project.is_backlog_activated = False
- project.is_kanban_activated = True
- project.is_wiki_activated = False
- project.is_issues_activated = False
- project.save()
-
- us_status = project.us_statuses.get(order=1)
- us_status.name = "To do"
- us_status.color = "#999999"
- us_status.save()
-
- us_status = project.us_statuses.get(order=2)
- us_status.name = "Doing"
- us_status.color = "#ff9900"
- us_status.is_closed = False
- us_status.save()
-
- us_status = UserStoryStatus()
- us_status.order = 3
- us_status.name = "Done"
- us_status.color = "#ffcc00"
- us_status.project = project
- us_status.is_closed = True
- us_status.save()
diff --git a/taiga/projects/tests/tests_api.py b/taiga/projects/tests/tests_api.py
index 44651753..37bba1f2 100644
--- a/taiga/projects/tests/tests_api.py
+++ b/taiga/projects/tests/tests_api.py
@@ -24,14 +24,15 @@ from django.core import mail
from django.db.models import get_model
from taiga.users.tests import create_user
-from taiga.projects.models import Project, Membership
+from taiga.projects.models import Project, Membership, ProjectTemplate
+from taiga.domains.models import Domain
from . import create_project
from . import add_membership
class ProfileTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1, is_superuser=True)
@@ -182,7 +183,7 @@ class ProfileTestCase(test.TestCase):
class ProjectsTestCase(test.TestCase):
- fixtures = ["initial_role.json", "initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1)
@@ -462,3 +463,264 @@ class ProjectsTestCase(test.TestCase):
self.assertEqual(response.status_code, 404)
self.assertEqual(Project.objects.all().count(), 4)
self.client.logout()
+
+class ProjectTemplatesTestCase(test.TestCase):
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
+
+ def setUp(self):
+ self.user1 = create_user(1)
+ self.user2 = create_user(2)
+ self.domain = Domain.objects.all()[0]
+ self.domain.owner = self.user1
+
+ def test_list_project_templates_by_anon(self):
+ response = self.client.get(reverse("project-templates-list"))
+ self.assertEqual(response.status_code, 401)
+
+ def test_list_project_templates_by_domain_owner(self):
+ response = self.client.login(username=self.user1.username,
+ password=self.user1.username)
+ self.assertTrue(response)
+ response = self.client.get(reverse("project-templates-list"))
+ self.assertEqual(response.status_code, 200)
+ projects_list = response.data
+ self.assertEqual(len(projects_list), 2)
+ self.client.logout()
+
+ def test_list_projects_by_not_domain_owner(self):
+ response = self.client.login(username=self.user2.username,
+ password=self.user2.username)
+ self.assertTrue(response)
+ response = self.client.get(reverse("project-templates-list"))
+ self.assertEqual(response.status_code, 403)
+
+ def test_view_project_template_by_anon(self):
+ response = self.client.get(reverse("project-templates-detail", args=(1,)))
+ self.assertEqual(response.status_code, 401)
+
+ def test_view_project_template_by_domain_owner(self):
+ response = self.client.login(username=self.user1.username,
+ password=self.user1.username)
+ self.assertTrue(response)
+ response = self.client.get(reverse("project-templates-detail", args=(1,)))
+ self.assertEqual(response.status_code, 200)
+ response = self.client.get(reverse("project-templates-detail", args=(2,)))
+ self.assertEqual(response.status_code, 200)
+ self.client.logout()
+
+ def test_view_project_template_by_not_domain_owner(self):
+ response = self.client.login(username=self.user2.username,
+ password=self.user2.username)
+ self.assertTrue(response)
+ response = self.client.get(reverse("project-templates-detail", args=(1,)))
+ self.assertEqual(response.status_code, 403)
+ response = self.client.get(reverse("project-templates-detail", args=(2,)))
+ self.assertEqual(response.status_code, 403)
+ self.client.logout()
+
+ def test_create_project_template_by_anon(self):
+ data = {
+ "name": "Test Project Template",
+ "slug": "test-project-template",
+ "description": "A new Test Project Template",
+ }
+
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+ response = self.client.post(
+ reverse("project-templates-list"),
+ json.dumps(data),
+ content_type="application/json")
+ self.assertEqual(response.status_code, 401)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+
+ data = {
+ "name": "Test Project Template 2",
+ "slug": "test-project-template-2",
+ "description": "A new Test Project Template",
+ "domain": 100,
+ }
+
+ response = self.client.post(
+ reverse("project-templates-list"),
+ json.dumps(data),
+ content_type="application/json")
+ self.assertEqual(response.status_code, 401)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+
+ def test_create_project_template_by_domain_owner(self):
+ data = {
+ "name": "Test Project Template",
+ "slug": "test-project-template",
+ "description": "A new Test Project Template",
+ }
+
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+ response = self.client.login(username=self.user1.username,
+ password=self.user1.username)
+ self.assertTrue(response)
+ response = self.client.post(reverse("project-templates-list"), json.dumps(data),
+ content_type="application/json")
+
+ self.assertEqual(response.status_code, 201)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 3)
+
+ data = {
+ "name": "Test Project Template 2",
+ "slug": "test-project-template-2",
+ "description": "A new Test Project Template",
+ "domain": 100,
+ }
+
+ response = self.client.login(username=self.user1.username,
+ password=self.user1.username)
+ self.assertTrue(response)
+ response = self.client.post(reverse("project-templates-list"), json.dumps(data),
+ content_type="application/json")
+
+ self.assertEqual(response.status_code, 201)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 4)
+ template = ProjectTemplate.objects.order_by('-created_date')[0]
+ self.assertEqual(template.domain.id, 1)
+
+ self.client.logout()
+
+ ProjectTemplate.objects.order_by("created_date")[0:2].delete()
+
+ def test_create_project_template_by_not_domain_owner(self):
+ data = {
+ "name": "Test Project Template",
+ "slug": "test-project-template",
+ "description": "A new Test Project Template",
+ }
+
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+ response = self.client.login(username=self.user1.username,
+ password=self.user1.username)
+ self.assertTrue(response)
+ response = self.client.post(
+ reverse("project-templates-list"),
+ json.dumps(data),
+ content_type="application/json")
+ self.assertEqual(response.status_code, 401)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+
+ data = {
+ "name": "Test Project Template 2",
+ "slug": "test-project-template-2",
+ "description": "A new Test Project Template",
+ "domain": 100
+ }
+
+ response = self.client.post(
+ reverse("project-templates-list"),
+ json.dumps(data),
+ content_type="application/json")
+ self.assertEqual(response.status_code, 401)
+ self.assertEqual(ProjectTemplate.objects.all().count(), 2)
+ self.client.logout()
+
+ # def test_edit_project_by_anon(self):
+ # data = {
+ # "description": "Edited project description",
+ # }
+
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.assertNotEqual(data["description"], self.project1.description)
+ # response = self.client.patch(
+ # reverse("projects-detail", args=(self.project1.id,)),
+ # json.dumps(data),
+ # content_type="application/json")
+ # self.assertEqual(response.status_code, 401)
+ # self.assertEqual(Project.objects.all().count(), 4)
+
+ # def test_edit_project_by_owner(self):
+ # data = {
+ # "description": "Modified project description",
+ # }
+
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.assertNotEqual(data["description"], self.project1.description)
+ # response = self.client.login(username=self.user1.username,
+ # password=self.user1.username)
+ # self.assertTrue(response)
+ # response = self.client.patch(
+ # reverse("projects-detail", args=(self.project1.id,)),
+ # json.dumps(data),
+ # content_type="application/json")
+ # self.assertEqual(response.status_code, 200)
+ # self.assertEqual(data["description"], response.data["description"])
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.client.logout()
+
+ # def test_edit_project_by_membership(self):
+ # data = {
+ # "description": "Edited project description",
+ # }
+
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.assertNotEqual(data["description"], self.project1.description)
+ # response = self.client.login(username=self.user3.username,
+ # password=self.user3.username)
+ # self.assertTrue(response)
+ # response = self.client.patch(
+ # reverse("projects-detail", args=(self.project1.id,)),
+ # json.dumps(data),
+ # content_type="application/json")
+ # self.assertEqual(response.status_code, 200)
+ # self.assertEqual(data["description"], response.data["description"])
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.client.logout()
+
+ # def test_edit_project_by_not_membership(self):
+ # data = {
+ # "description": "Edited project description",
+ # }
+
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.assertNotEqual(data["description"], self.project1.description)
+ # response = self.client.login(username=self.user2.username,
+ # password=self.user2.username)
+ # self.assertTrue(response)
+ # response = self.client.patch(
+ # reverse("projects-detail", args=(self.project1.id,)),
+ # json.dumps(data),
+ # content_type="application/json")
+ # self.assertEqual(response.status_code, 404)
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.client.logout()
+
+ # def test_delete_project_by_anon(self):
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
+ # self.assertEqual(response.status_code, 401)
+ # self.assertEqual(Project.objects.all().count(), 4)
+
+ # def test_delete_project_by_owner(self):
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # response = self.client.login(username=self.user1.username,
+ # password=self.user1.username)
+ # self.assertTrue(response)
+ # response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
+ # self.assertEqual(response.status_code, 204)
+ # self.assertEqual(Project.objects.all().count(), 3)
+ # self.client.logout()
+
+ # def test_delete_project_by_membership(self):
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # response = self.client.login(username=self.user3.username,
+ # password=self.user3.username)
+ # self.assertTrue(response)
+ # response = self.client.delete(reverse("projects-detail", args=(self.project1.id,)))
+ # self.assertEqual(response.status_code, 403)
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.client.logout()
+
+ # def test_delete_project_by_not_membership(self):
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # response = self.client.login(username=self.user1.username,
+ # password=self.user1.username)
+ # self.assertTrue(response)
+ # response = self.client.delete(reverse("projects-detail", args=(self.project3.id,)))
+ # self.assertEqual(response.status_code, 404)
+ # self.assertEqual(Project.objects.all().count(), 4)
+ # self.client.logout()
diff --git a/taiga/projects/tests/tests_notifications.py b/taiga/projects/tests/tests_notifications.py
index f20cff71..ae33602e 100644
--- a/taiga/projects/tests/tests_notifications.py
+++ b/taiga/projects/tests/tests_notifications.py
@@ -32,7 +32,7 @@ from . import create_project
from . import add_membership
class AllProjectEventsNotificationsTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1)
@@ -82,7 +82,7 @@ class AllProjectEventsNotificationsTestCase(test.TestCase):
self.user1.save()
class OnlyAssigendNotificationsTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1)
@@ -153,7 +153,7 @@ class OnlyAssigendNotificationsTestCase(test.TestCase):
self.user1.save()
class OnlyOwnerNotificationsTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1)
diff --git a/taiga/projects/userstories/tests/tests_api.py b/taiga/projects/userstories/tests/tests_api.py
index 5f8a4c70..fe396610 100644
--- a/taiga/projects/userstories/tests/tests_api.py
+++ b/taiga/projects/userstories/tests/tests_api.py
@@ -33,7 +33,7 @@ import reversion
class UserStoriesTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1) # Project owner
diff --git a/taiga/projects/userstories/tests/tests_services.py b/taiga/projects/userstories/tests/tests_services.py
index dd5d1814..2c90daa9 100644
--- a/taiga/projects/userstories/tests/tests_services.py
+++ b/taiga/projects/userstories/tests/tests_services.py
@@ -26,7 +26,7 @@ from . import create_userstory
class UserStoriesServiceTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1) # Project owner
diff --git a/taiga/projects/wiki/tests/tests_api.py b/taiga/projects/wiki/tests/tests_api.py
index 4a5ef876..90936111 100644
--- a/taiga/projects/wiki/tests/tests_api.py
+++ b/taiga/projects/wiki/tests/tests_api.py
@@ -30,7 +30,7 @@ from . import create_wiki_page
class WikiPagesTestCase(test.TestCase):
- fixtures = ["initial_domains.json"]
+ fixtures = ["initial_domains.json", "initial_project_templates.json"]
def setUp(self):
self.user1 = create_user(1)
diff --git a/taiga/routers.py b/taiga/routers.py
index d9f50b27..e1f80c6b 100644
--- a/taiga/routers.py
+++ b/taiga/routers.py
@@ -64,9 +64,11 @@ from taiga.projects.api import IssueStatusViewSet
from taiga.projects.api import IssueTypeViewSet
from taiga.projects.api import PriorityViewSet
from taiga.projects.api import SeverityViewSet
+from taiga.projects.api import ProjectTemplateViewSet
router.register(r"roles", RolesViewSet, base_name="roles")
router.register(r"projects", ProjectViewSet, base_name="projects")
+router.register(r"project-templates", ProjectTemplateViewSet, base_name="project-templates")
router.register(r"memberships", MembershipViewSet, base_name="memberships")
router.register(r"invitations", InvitationViewSet, base_name="invitations")
router.register(r"userstory-statuses", UserStoryStatusViewSet, base_name="userstory-statuses")