User settings by project
parent
8bfdf596dc
commit
e382c04547
|
@ -309,6 +309,7 @@ INSTALLED_APPS = [
|
||||||
"taiga.projects.issues",
|
"taiga.projects.issues",
|
||||||
"taiga.projects.wiki",
|
"taiga.projects.wiki",
|
||||||
"taiga.projects.contact",
|
"taiga.projects.contact",
|
||||||
|
"taiga.projects.settings",
|
||||||
"taiga.searches",
|
"taiga.searches",
|
||||||
"taiga.timeline",
|
"taiga.timeline",
|
||||||
"taiga.mdrender",
|
"taiga.mdrender",
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from django.db.models import Q
|
||||||
|
|
||||||
|
from taiga.base import response
|
||||||
|
from taiga.base.api import ModelCrudViewSet, ReadOnlyListViewSet
|
||||||
|
|
||||||
|
from taiga.projects.settings.choices import HOMEPAGE_CHOICES
|
||||||
|
from taiga.projects.models import Project
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import permissions
|
||||||
|
from . import serializers
|
||||||
|
from . import services
|
||||||
|
|
||||||
|
|
||||||
|
class UserProjectSettingsViewSet(ModelCrudViewSet):
|
||||||
|
serializer_class = serializers.UserProjectSettingsSerializer
|
||||||
|
permission_classes = (permissions.UserProjectSettingsPermission,)
|
||||||
|
|
||||||
|
def _build_user_project_settings(self):
|
||||||
|
projects = Project.objects.filter(
|
||||||
|
Q(owner=self.request.user) |
|
||||||
|
Q(memberships__user=self.request.user)
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
for project in projects:
|
||||||
|
services.create_user_project_settings_if_not_exists(
|
||||||
|
project, self.request.user)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
if self.request.user.is_anonymous():
|
||||||
|
return models.UserProjectSettings.objects.none()
|
||||||
|
|
||||||
|
self._build_user_project_settings()
|
||||||
|
|
||||||
|
return models.UserProjectSettings.objects.filter(user=self.request.user)\
|
||||||
|
.filter(
|
||||||
|
Q(project__owner=self.request.user) |
|
||||||
|
Q(project__memberships__user=self.request.user)
|
||||||
|
).distinct()
|
||||||
|
|
||||||
|
|
||||||
|
class SectionsViewSet(ReadOnlyListViewSet):
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
return response.Response(HOMEPAGE_CHOICES)
|
|
@ -0,0 +1,45 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import enum
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class Section(enum.IntEnum):
|
||||||
|
timeline = 1
|
||||||
|
search = 2
|
||||||
|
backlog = 3
|
||||||
|
kanban = 4
|
||||||
|
issues = 5
|
||||||
|
wiki = 6
|
||||||
|
team = 7
|
||||||
|
meetup = 8
|
||||||
|
admin = 9
|
||||||
|
|
||||||
|
|
||||||
|
HOMEPAGE_CHOICES = (
|
||||||
|
(Section.timeline, _("Timeline")),
|
||||||
|
(Section.search, _("Search")),
|
||||||
|
(Section.backlog, _("Backlog")),
|
||||||
|
(Section.kanban, _("Kanban")),
|
||||||
|
(Section.issues, _("Issues")),
|
||||||
|
(Section.wiki, _("TeamWiki")),
|
||||||
|
(Section.team, _("Team")),
|
||||||
|
(Section.meetup, _("Meet Up")),
|
||||||
|
(Section.admin, _("Admin")),
|
||||||
|
)
|
|
@ -0,0 +1,49 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from .choices import HOMEPAGE_CHOICES, Section
|
||||||
|
|
||||||
|
|
||||||
|
class UserProjectSettings(models.Model):
|
||||||
|
"""
|
||||||
|
This class represents a persistence for
|
||||||
|
project user notifications preference.
|
||||||
|
"""
|
||||||
|
project = models.ForeignKey("projects.Project", related_name="user_project_settings")
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="user_project_settings")
|
||||||
|
homepage = models.SmallIntegerField(choices=HOMEPAGE_CHOICES,
|
||||||
|
default=Section.timeline)
|
||||||
|
|
||||||
|
created_at = models.DateTimeField(default=timezone.now)
|
||||||
|
modified_at = models.DateTimeField()
|
||||||
|
_importing = None
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ("project", "user",)
|
||||||
|
ordering = ["created_at"]
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self._importing or not self.modified_date:
|
||||||
|
self.modified_at = timezone.now()
|
||||||
|
|
||||||
|
return super().save(*args, **kwargs)
|
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from taiga.base.api.permissions import (TaigaResourcePermission, IsAuthenticated)
|
||||||
|
|
||||||
|
|
||||||
|
class UserProjectSettingsPermission(TaigaResourcePermission):
|
||||||
|
retrieve_perms = IsAuthenticated()
|
||||||
|
create_perms = IsAuthenticated()
|
||||||
|
update_perms = IsAuthenticated()
|
||||||
|
partial_update_perms = IsAuthenticated()
|
||||||
|
destroy_perms = IsAuthenticated()
|
||||||
|
list_perms = IsAuthenticated()
|
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from taiga.base.api import serializers
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
class UserProjectSettingsSerializer(serializers.ModelSerializer):
|
||||||
|
project_name = serializers.SerializerMethodField("get_project_name")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.UserProjectSettings
|
||||||
|
fields = ('id', 'project', 'project_name', 'homepage')
|
||||||
|
|
||||||
|
def get_project_name(self, obj):
|
||||||
|
return obj.project.name
|
|
@ -0,0 +1,67 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
|
from django.db import IntegrityError
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from taiga.base import exceptions as exc
|
||||||
|
from taiga.projects.settings.choices import Section
|
||||||
|
|
||||||
|
|
||||||
|
def user_project_settings_exists(project, user) -> bool:
|
||||||
|
"""
|
||||||
|
Check if policy exists for specified project
|
||||||
|
and user.
|
||||||
|
"""
|
||||||
|
model_cls = apps.get_model("settings", "UserProjectSettings")
|
||||||
|
qs = model_cls.objects.filter(project=project,
|
||||||
|
user=user)
|
||||||
|
return qs.exists()
|
||||||
|
|
||||||
|
|
||||||
|
def create_user_project_settings(project, user, homepage=Section.timeline):
|
||||||
|
"""
|
||||||
|
Given a project and user, create notification policy for it.
|
||||||
|
"""
|
||||||
|
model_cls = apps.get_model("settings", "UserProjectSettings")
|
||||||
|
try:
|
||||||
|
return model_cls.objects.create(project=project,
|
||||||
|
user=user,
|
||||||
|
homepage=homepage)
|
||||||
|
except IntegrityError as e:
|
||||||
|
raise exc.IntegrityError(
|
||||||
|
_("Notify exists for specified user and project")) from e
|
||||||
|
|
||||||
|
|
||||||
|
def create_user_project_settings_if_not_exists(project, user,
|
||||||
|
homepage=Section.timeline):
|
||||||
|
"""
|
||||||
|
Given a project and user, create notification policy for it.
|
||||||
|
"""
|
||||||
|
model_cls = apps.get_model("settings", "UserProjectSettings")
|
||||||
|
try:
|
||||||
|
result = model_cls.objects.get_or_create(
|
||||||
|
project=project,
|
||||||
|
user=user,
|
||||||
|
defaults={"homepage": homepage}
|
||||||
|
)
|
||||||
|
return result[0]
|
||||||
|
except IntegrityError as e:
|
||||||
|
raise exc.IntegrityError(
|
||||||
|
_("Notify exists for specified user and project")) from e
|
|
@ -49,6 +49,13 @@ from taiga.projects.notifications.api import NotifyPolicyViewSet
|
||||||
router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notifications")
|
router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notifications")
|
||||||
|
|
||||||
|
|
||||||
|
# Project settings
|
||||||
|
from taiga.projects.settings.api import UserProjectSettingsViewSet, SectionsViewSet
|
||||||
|
|
||||||
|
router.register(r"user-project-settings", UserProjectSettingsViewSet, base_name="user-project-settings")
|
||||||
|
router.register(r"sections", SectionsViewSet, base_name="sections")
|
||||||
|
|
||||||
|
|
||||||
# Projects & Selectors
|
# Projects & Selectors
|
||||||
from taiga.projects.api import ProjectViewSet
|
from taiga.projects.api import ProjectViewSet
|
||||||
from taiga.projects.api import ProjectFansViewSet
|
from taiga.projects.api import ProjectFansViewSet
|
||||||
|
|
Loading…
Reference in New Issue