User settings by project

remotes/origin/4.0rc
Daniel García 2018-09-18 13:41:35 +02:00 committed by Alex Hermida
parent 8bfdf596dc
commit e382c04547
10 changed files with 291 additions and 0 deletions

View File

@ -309,6 +309,7 @@ INSTALLED_APPS = [
"taiga.projects.issues",
"taiga.projects.wiki",
"taiga.projects.contact",
"taiga.projects.settings",
"taiga.searches",
"taiga.timeline",
"taiga.mdrender",

View File

View File

@ -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)

View File

@ -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")),
)

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -49,6 +49,13 @@ from taiga.projects.notifications.api import NotifyPolicyViewSet
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
from taiga.projects.api import ProjectViewSet
from taiga.projects.api import ProjectFansViewSet