From 0f9ae58fb20c6391fcfa81e42855bc89aaae6098 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 8 Jun 2015 16:22:15 +0100 Subject: [PATCH 001/190] Adding field_type to custom attributes model, with associated migration --- .../0006_add_customattribute_field_type.py | 33 +++++++++++++++++++ taiga/projects/custom_attributes/models.py | 6 ++++ 2 files changed, 39 insertions(+) create mode 100644 taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py diff --git a/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py b/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py new file mode 100644 index 00000000..ff3a3844 --- /dev/null +++ b/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('custom_attributes', '0005_auto_20150505_1639'), + ] + + operations = [ + migrations.AddField( + model_name='issuecustomattribute', + name='field_type', + field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + preserve_default=True, + ), + migrations.AddField( + model_name='taskcustomattribute', + name='field_type', + field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + preserve_default=True, + ), + migrations.AddField( + model_name='userstorycustomattribute', + name='field_type', + field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + preserve_default=True, + ), + + ] diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index 6f82244d..bde84bc0 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -27,9 +27,15 @@ from taiga.projects.occ.mixins import OCCModelMixin # Custom Attribute Models ####################################################### + class AbstractCustomAttribute(models.Model): + FIELD_TYPES = ( + ('TEXT', 'Text'), + ('MULTI', 'Multi-Line Text') + ) name = models.CharField(null=False, blank=False, max_length=64, verbose_name=_("name")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) + field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, verbose_name=_("type")) order = models.IntegerField(null=False, blank=False, default=10000, verbose_name=_("order")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="%(class)ss", verbose_name=_("project")) From 85b24322af8783ef2c1fdfac815e08552c997669 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Thu, 18 Jun 2015 16:10:26 +0100 Subject: [PATCH 002/190] Add a default value of TEXT to the new field_type attribute on the custom attribute model --- taiga/projects/custom_attributes/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index bde84bc0..b9ea807d 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -35,7 +35,7 @@ class AbstractCustomAttribute(models.Model): ) name = models.CharField(null=False, blank=False, max_length=64, verbose_name=_("name")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) - field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, verbose_name=_("type")) + field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, default='TEXT', verbose_name=_("type")) order = models.IntegerField(null=False, blank=False, default=10000, verbose_name=_("order")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="%(class)ss", verbose_name=_("project")) From debf6417675167ce4a7f1f680a9429bdcbb205fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 18 Jun 2015 17:45:07 +0200 Subject: [PATCH 003/190] Make minor improvements over Membership and Project serializers --- taiga/projects/issues/serializers.py | 2 +- taiga/projects/serializers.py | 19 ++++++++----------- taiga/projects/tasks/serializers.py | 2 +- taiga/projects/userstories/serializers.py | 2 +- taiga/users/serializers.py | 3 ++- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index fc43a8f8..dd0d4ef5 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -24,7 +24,7 @@ from taiga.mdrender.service import render as mdrender from taiga.projects.validators import ProjectExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicIssueStatusSerializer -from taiga.users.serializers import BasicInfoSerializer as UserBasicInfoSerializer +from taiga.users.serializers import UserBasicInfoSerializer from . import models diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 5aa7c019..5ca3e7f4 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -27,6 +27,7 @@ from taiga.base.fields import TagsColorsField from taiga.users.services import get_photo_or_gravatar_url from taiga.users.serializers import UserSerializer +from taiga.users.serializers import UserBasicInfoSerializer from taiga.users.serializers import ProjectRoleSerializer from taiga.users.validators import RoleExistsValidator @@ -197,7 +198,7 @@ class MembershipSerializer(serializers.ModelSerializer): photo = serializers.SerializerMethodField("get_photo") project_name = serializers.SerializerMethodField("get_project_name") project_slug = serializers.SerializerMethodField("get_project_slug") - invited_by = UserSerializer(read_only=True) + invited_by = UserBasicInfoSerializer(read_only=True) class Meta: model = models.Membership @@ -346,11 +347,11 @@ class ProjectSerializer(serializers.ModelSerializer): class ProjectDetailSerializer(ProjectSerializer): - roles = serializers.SerializerMethodField("get_roles") - memberships = serializers.SerializerMethodField("get_memberships") us_statuses = UserStoryStatusSerializer(many=True, required=False) # User Stories points = PointsSerializer(many=True, required=False) + task_statuses = TaskStatusSerializer(many=True, required=False) # Tasks + issue_statuses = IssueStatusSerializer(many=True, required=False) issue_types = IssueTypeSerializer(many=True, required=False) priorities = PrioritySerializer(many=True, required=False) # Issues @@ -362,7 +363,10 @@ class ProjectDetailSerializer(ProjectSerializer): many=True, required=False) issue_custom_attributes = IssueCustomAttributeSerializer(source="issuecustomattributes", many=True, required=False) - users = serializers.SerializerMethodField("get_users") + + roles = ProjectRoleSerializer(source="roles", many=True, read_only=True) + users = UserSerializer(source="members", many=True, read_only=True) + memberships = serializers.SerializerMethodField(method_name="get_memberships") def get_memberships(self, obj): qs = obj.memberships.filter(user__isnull=False) @@ -372,13 +376,6 @@ class ProjectDetailSerializer(ProjectSerializer): serializer = ProjectMembershipSerializer(qs, many=True) return serializer.data - def get_roles(self, obj): - serializer = ProjectRoleSerializer(obj.roles.all(), many=True) - return serializer.data - - def get_users(self, obj): - return UserSerializer(obj.members.all(), many=True).data - class ProjectDetailAdminSerializer(ProjectDetailSerializer): class Meta: diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 0c8e436d..30a63d1b 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -27,7 +27,7 @@ from taiga.projects.milestones.validators import SprintExistsValidator from taiga.projects.tasks.validators import TaskExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicTaskStatusSerializerSerializer -from taiga.users.serializers import BasicInfoSerializer as UserBasicInfoSerializer +from taiga.users.serializers import UserBasicInfoSerializer from . import models diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 3978381e..30129e7b 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -27,7 +27,7 @@ from taiga.projects.validators import UserStoryStatusExistsValidator from taiga.projects.userstories.validators import UserStoryExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicUserStoryStatusSerializer -from taiga.users.serializers import BasicInfoSerializer as UserBasicInfoSerializer +from taiga.users.serializers import UserBasicInfoSerializer from . import models diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 934c0677..1a22ff70 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -108,11 +108,12 @@ class UserAdminSerializer(UserSerializer): read_only_fields = ("id", "email") -class BasicInfoSerializer(UserSerializer): +class UserBasicInfoSerializer(UserSerializer): class Meta: model = User fields = ("username", "full_name_display","photo", "big_photo") + class RecoverySerializer(serializers.Serializer): token = serializers.CharField(max_length=200) password = serializers.CharField(min_length=6) From aff9a7d63704c4e45b5f0c1b5136d46894a1a6d9 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 19 Jun 2015 10:42:03 +0200 Subject: [PATCH 004/190] Fixing created datetime for timeline entries --- .../management/commands/rebuild_timeline.py | 13 ++++------ .../rebuild_timeline_for_user_creation.py | 7 +++-- taiga/timeline/service.py | 15 ++++++----- taiga/timeline/signals.py | 25 +++++++++++------- tests/integration/test_timeline.py | 26 +++++++++---------- tests/unit/test_timeline.py | 16 ++++++------ 6 files changed, 52 insertions(+), 50 deletions(-) diff --git a/taiga/timeline/management/commands/rebuild_timeline.py b/taiga/timeline/management/commands/rebuild_timeline.py index 0a234d38..2c462ce0 100644 --- a/taiga/timeline/management/commands/rebuild_timeline.py +++ b/taiga/timeline/management/commands/rebuild_timeline.py @@ -61,7 +61,7 @@ class BulkCreator(object): bulk_creator = BulkCreator() -def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, namespace:str="default", extra_data:dict={}): +def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, created_datetime:object, namespace:str="default", extra_data:dict={}): assert isinstance(obj, Model), "obj must be a instance of Model" assert isinstance(instance, Model), "instance must be a instance of Model" event_type_key = _get_impl_key_from_model(instance.__class__, event_type) @@ -73,8 +73,8 @@ def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, n event_type=event_type_key, project=instance.project, data=impl(instance, extra_data=extra_data), - data_content_type = ContentType.objects.get_for_model(instance.__class__), - created = bulk_creator.created, + data_content_type=ContentType.objects.get_for_model(instance.__class__), + created=created_datetime, )) @@ -116,23 +116,20 @@ def generate_timeline(initial_date, final_date, project_id): #Memberships for membership in project.memberships.exclude(user=None).exclude(user=project.owner): - bulk_creator.created = membership.created_at - _push_to_timelines(project, membership.user, membership, "create") + _push_to_timelines(project, membership.user, membership, "create", membership.created_at) for project in projects.iterator(): - bulk_creator.created = project.created_date print("Project:", bulk_creator.created) extra_data = { "values_diff": {}, "user": extract_user_info(project.owner), } - _push_to_timelines(project, project.owner, project, "create", extra_data=extra_data) + _push_to_timelines(project, project.owner, project, "create", project.created_date, extra_data=extra_data) del extra_data for historyEntry in history_entries.iterator(): print("History entry:", historyEntry.created_at) try: - bulk_creator.created = historyEntry.created_at on_new_history_entry(None, historyEntry, None) except ObjectDoesNotExist as e: print("Ignoring") diff --git a/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py b/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py index d4f99ed5..2982969a 100644 --- a/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py +++ b/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py @@ -53,7 +53,7 @@ class BulkCreator(object): bulk_creator = BulkCreator() -def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, namespace:str="default", extra_data:dict={}): +def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, created_datetime:object, namespace:str="default", extra_data:dict={}): assert isinstance(obj, Model), "obj must be a instance of Model" assert isinstance(instance, Model), "instance must be a instance of Model" event_type_key = _get_impl_key_from_model(instance.__class__, event_type) @@ -66,7 +66,7 @@ def custom_add_to_object_timeline(obj:object, instance:object, event_type:str, n project=None, data=impl(instance, extra_data=extra_data), data_content_type = ContentType.objects.get_for_model(instance.__class__), - created = bulk_creator.created, + created=created_datetime, )) @@ -75,13 +75,12 @@ def generate_timeline(): # Users api wasn't a HistoryResourceMixin so we can't interate on the HistoryEntries in this case users = User.objects.order_by("date_joined") for user in users.iterator(): - bulk_creator.created = user.date_joined print("User:", user.date_joined) extra_data = { "values_diff": {}, "user": extract_user_info(user), } - _push_to_timelines(None, user, user, "create", extra_data=extra_data) + _push_to_timelines(None, user, user, "create", user.date_joined, extra_data=extra_data) del extra_data bulk_creator.flush() diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index 5317942f..6fde769c 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -50,7 +50,7 @@ def build_project_namespace(project:object): return "{0}:{1}".format("project", project.id) -def _add_to_object_timeline(obj:object, instance:object, event_type:str, namespace:str="default", extra_data:dict={}): +def _add_to_object_timeline(obj:object, instance:object, event_type:str, created_datetime:object, namespace:str="default", extra_data:dict={}): assert isinstance(obj, Model), "obj must be a instance of Model" assert isinstance(instance, Model), "instance must be a instance of Model" from .models import Timeline @@ -67,21 +67,22 @@ def _add_to_object_timeline(obj:object, instance:object, event_type:str, namespa event_type=event_type_key, project=project, data=impl(instance, extra_data=extra_data), - data_content_type = ContentType.objects.get_for_model(instance.__class__), + data_content_type=ContentType.objects.get_for_model(instance.__class__), + created=created_datetime, ) -def _add_to_objects_timeline(objects, instance:object, event_type:str, namespace:str="default", extra_data:dict={}): +def _add_to_objects_timeline(objects, instance:object, event_type:str, created_datetime:object, namespace:str="default", extra_data:dict={}): for obj in objects: - _add_to_object_timeline(obj, instance, event_type, namespace, extra_data) + _add_to_object_timeline(obj, instance, event_type, created_datetime, namespace, extra_data) @app.task -def push_to_timeline(objects, instance:object, event_type:str, namespace:str="default", extra_data:dict={}): +def push_to_timeline(objects, instance:object, event_type:str, created_datetime:object, namespace:str="default", extra_data:dict={}): if isinstance(objects, Model): - _add_to_object_timeline(objects, instance, event_type, namespace, extra_data) + _add_to_object_timeline(objects, instance, event_type, created_datetime, namespace, extra_data) elif isinstance(objects, QuerySet) or isinstance(objects, list): - _add_to_objects_timeline(objects, instance, event_type, namespace, extra_data) + _add_to_objects_timeline(objects, instance, event_type, created_datetime, namespace, extra_data) else: raise Exception("Invalid objects parameter") diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 03d8b9f5..b0787946 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -15,6 +15,7 @@ # along with this program. If not, see . from django.conf import settings +from django.utils import timezone from taiga.projects.history import services as history_services from taiga.projects.models import Project @@ -33,15 +34,15 @@ def _push_to_timeline(*args, **kwargs): push_to_timeline(*args, **kwargs) -def _push_to_timelines(project, user, obj, event_type, extra_data={}): +def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data={}): if project is not None: # Project timeline - _push_to_timeline(project, obj, event_type, + _push_to_timeline(project, obj, event_type, created_datetime, namespace=build_project_namespace(project), extra_data=extra_data) # User timeline - _push_to_timeline(user, obj, event_type, + _push_to_timeline(user, obj, event_type, created_datetime, namespace=build_user_namespace(user), extra_data=extra_data) @@ -64,7 +65,7 @@ def _push_to_timelines(project, user, obj, event_type, extra_data={}): related_people |= team related_people = related_people.distinct() - _push_to_timeline(related_people, obj, event_type, + _push_to_timeline(related_people, obj, event_type, created_datetime, namespace=build_user_namespace(user), extra_data=extra_data) @@ -103,7 +104,8 @@ def on_new_history_entry(sender, instance, created, **kwargs): if instance.delete_comment_date: extra_data["comment_deleted"] = True - _push_to_timelines(project, user, obj, event_type, extra_data=extra_data) + created_datetime = instance.created_at + _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data=extra_data) def create_membership_push_to_timeline(sender, instance, **kwargs): @@ -111,26 +113,29 @@ def create_membership_push_to_timeline(sender, instance, **kwargs): # If the user is the project owner we don't do anything because that info will # be shown in created project timeline entry if not instance.pk and instance.user and instance.user != instance.project.owner: - _push_to_timelines(instance.project, instance.user, instance, "create") + created_datetime = instance.created_at + _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) #Updating existing membership elif instance.pk: prev_instance = sender.objects.get(pk=instance.pk) if instance.user != prev_instance.user: + created_datetime = timezone.now() # The new member - _push_to_timelines(instance.project, instance.user, instance, "create") + _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) # If we are updating the old user is removed from project if prev_instance.user: - _push_to_timelines(instance.project, prev_instance.user, prev_instance, "delete") + _push_to_timelines(instance.project, prev_instance.user, prev_instance, "delete", created_datetime) def delete_membership_push_to_timeline(sender, instance, **kwargs): if instance.user: - _push_to_timelines(instance.project, instance.user, instance, "delete") + created_datetime = timezone.now() + _push_to_timelines(instance.project, instance.user, instance, "delete", created_datetime) def create_user_push_to_timeline(sender, instance, created, **kwargs): if created: project = None user = instance - _push_to_timelines(project, user, user, "create") + _push_to_timelines(project, user, user, "create", created_datetime=user.date_joined) diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 326a7b85..05c649b7 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -34,7 +34,7 @@ def test_add_to_object_timeline(): service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task, "test") + service._add_to_object_timeline(user1, task, "test", task.created_date) assert Timeline.objects.filter(object_id=user1.id).count() == 2 assert Timeline.objects.order_by("-id")[0].data == id(task) @@ -53,11 +53,11 @@ def test_get_timeline(): service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task1, "test") - service._add_to_object_timeline(user1, task2, "test") - service._add_to_object_timeline(user1, task3, "test") - service._add_to_object_timeline(user1, task4, "test") - service._add_to_object_timeline(user2, task1, "test") + service._add_to_object_timeline(user1, task1, "test", task1.created_date) + service._add_to_object_timeline(user1, task2, "test", task2.created_date) + service._add_to_object_timeline(user1, task3, "test", task3.created_date) + service._add_to_object_timeline(user1, task4, "test", task4.created_date) + service._add_to_object_timeline(user2, task1, "test", task1.created_date) assert Timeline.objects.filter(object_id=user1.id).count() == 5 assert Timeline.objects.filter(object_id=user2.id).count() == 2 @@ -71,7 +71,7 @@ def test_filter_timeline_no_privileges(): task1= factories.TaskFactory() service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task1, "test") + service._add_to_object_timeline(user1, task1, "test", task1.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") timeline = service.filter_timeline_for_user(timeline, user2) assert timeline.count() == 0 @@ -86,8 +86,8 @@ def test_filter_timeline_public_project(): task2= factories.TaskFactory.create(project=project) service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task1, "test") - service._add_to_object_timeline(user1, task2, "test") + service._add_to_object_timeline(user1, task1, "test", task1.created_date) + service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") timeline = service.filter_timeline_for_user(timeline, user2) assert timeline.count() == 1 @@ -102,8 +102,8 @@ def test_filter_timeline_private_project_anon_permissions(): task2= factories.TaskFactory.create(project=project) service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task1, "test") - service._add_to_object_timeline(user1, task2, "test") + service._add_to_object_timeline(user1, task1, "test", task1.created_date) + service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") timeline = service.filter_timeline_for_user(timeline, user2) assert timeline.count() == 1 @@ -121,8 +121,8 @@ def test_filter_timeline_private_project_member_permissions(): task2= factories.TaskFactory.create(project=project) service.register_timeline_implementation("tasks.task", "test", lambda x, extra_data=None: str(id(x))) - service._add_to_object_timeline(user1, task1, "test") - service._add_to_object_timeline(user1, task2, "test") + service._add_to_object_timeline(user1, task1, "test", task1.created_date) + service._add_to_object_timeline(user1, task2, "test", task2.created_date) timeline = Timeline.objects.exclude(event_type="users.user.create") timeline = service.filter_timeline_for_user(timeline, user2) assert timeline.count() == 3 diff --git a/tests/unit/test_timeline.py b/tests/unit/test_timeline.py index c580404e..377e9728 100644 --- a/tests/unit/test_timeline.py +++ b/tests/unit/test_timeline.py @@ -31,12 +31,12 @@ def test_push_to_timeline_many_objects(): with patch("taiga.timeline.service._add_to_object_timeline") as mock: users = [User(), User(), User()] project = Project() - service.push_to_timeline(users, project, "test") + service.push_to_timeline(users, project, "test", project.created_date) assert mock.call_count == 3 assert mock.mock_calls == [ - call(users[0], project, "test", "default", {}), - call(users[1], project, "test", "default", {}), - call(users[2], project, "test", "default", {}), + call(users[0], project, "test", project.created_date, "default", {}), + call(users[1], project, "test", project.created_date, "default", {}), + call(users[2], project, "test", project.created_date, "default", {}), ] with pytest.raises(Exception): service.push_to_timeline(None, project, "test") @@ -46,12 +46,12 @@ def test_add_to_objects_timeline(): with patch("taiga.timeline.service._add_to_object_timeline") as mock: users = [User(), User(), User()] project = Project() - service._add_to_objects_timeline(users, project, "test") + service._add_to_objects_timeline(users, project, "test", project.created_date) assert mock.call_count == 3 assert mock.mock_calls == [ - call(users[0], project, "test", "default", {}), - call(users[1], project, "test", "default", {}), - call(users[2], project, "test", "default", {}), + call(users[0], project, "test", project.created_date, "default", {}), + call(users[1], project, "test", project.created_date, "default", {}), + call(users[2], project, "test", project.created_date, "default", {}), ] with pytest.raises(Exception): service.push_to_timeline(None, project, "test") From 15af8ef3ccbeddbf3c17de24678f6d4bc11c2790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 20 Jun 2015 22:24:48 +0200 Subject: [PATCH 005/190] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abbd0695..50f94187 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog # +## 1.9.0 ??? (unreleased) +### Features +- ... + +### Misc +- Lots of small and not so small bugfixes. + + ## 1.8.0 Saracenia Purpurea (2015-06-18) ### Features From 1bd2a6c0de6bcb993928195bb2209979e1560198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 20 Jun 2015 22:29:22 +0200 Subject: [PATCH 006/190] [i18n] Update locales --- taiga/locale/de/LC_MESSAGES/django.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 6ea5110a..cf2bc68f 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-17 08:05+0000\n" -"Last-Translator: Regina \n" +"PO-Revision-Date: 2015-06-20 14:15+0000\n" +"Last-Translator: Hans Raaf\n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -1441,7 +1441,7 @@ msgstr "Es gibt keinen Sprint mit dieser id" #: taiga/projects/mixins/blocked.py:29 msgid "is blocked" -msgstr "ist gesperrt" +msgstr "wird blockiert" #: taiga/projects/mixins/ordering.py:47 #, python-brace-format From 68181b8c5f18948730e86774b4a2b6e0e0299fe6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 23 Jun 2015 10:19:59 +0200 Subject: [PATCH 007/190] Fixing I18NJsonField --- taiga/base/fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/base/fields.py b/taiga/base/fields.py index 07898ea2..a1a8c56c 100644 --- a/taiga/base/fields.py +++ b/taiga/base/fields.py @@ -58,7 +58,7 @@ class I18NJsonField(JsonField): if key in self.i18n_fields: if isinstance(value, list): - i18n_d[key] = [e is not None and _(e) or e for e in value] + i18n_d[key] = [e is not None and _(str(e)) or e for e in value] if isinstance(value, str): i18n_d[key] = value is not None and _(value) or value else: From f94f52b8acb95d13b054a22e35357418aa9356e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 23 Jun 2015 18:57:50 +0200 Subject: [PATCH 008/190] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50f94187..5ec53387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## 1.9.0 ??? (unreleased) ### Features -- ... +- Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) ### Misc - Lots of small and not so small bugfixes. From 67bf755771ad798d8bbe77fec751782795264e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 23 Jun 2015 19:56:26 +0200 Subject: [PATCH 009/190] Fix migrations of custom_attributes --- .../migrations/0006_add_customattribute_field_type.py | 7 +++---- taiga/projects/custom_attributes/models.py | 6 +++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py b/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py index ff3a3844..8a4aebb8 100644 --- a/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py +++ b/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py @@ -14,20 +14,19 @@ class Migration(migrations.Migration): migrations.AddField( model_name='issuecustomattribute', name='field_type', - field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), preserve_default=True, ), migrations.AddField( model_name='taskcustomattribute', name='field_type', - field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), preserve_default=True, ), migrations.AddField( model_name='userstorycustomattribute', name='field_type', - field=models.CharField(default='TEXT', max_length=5, verbose_name='attribute field type'), + field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), preserve_default=True, ), - ] diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index b9ea807d..6d05121c 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -30,12 +30,12 @@ from taiga.projects.occ.mixins import OCCModelMixin class AbstractCustomAttribute(models.Model): FIELD_TYPES = ( - ('TEXT', 'Text'), - ('MULTI', 'Multi-Line Text') + ("TEXT", _("Text")), + ("MULTI", _("Multi-Line Text")) ) name = models.CharField(null=False, blank=False, max_length=64, verbose_name=_("name")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) - field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, default='TEXT', verbose_name=_("type")) + field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, default="TEXT", verbose_name=_("type")) order = models.IntegerField(null=False, blank=False, default=10000, verbose_name=_("order")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="%(class)ss", verbose_name=_("project")) From 8efeb406fd43a3ee80b9a9dff4c7d0a09d448e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 21:24:40 +0200 Subject: [PATCH 010/190] Adding more deterministic timeline ordering, by time and id --- taiga/timeline/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index 6fde769c..28327297 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -96,7 +96,7 @@ def get_timeline(obj, namespace=None): if namespace is not None: timeline = timeline.filter(namespace=namespace) - timeline = timeline.order_by("-created") + timeline = timeline.order_by("-created", "-id") return timeline From 91c9056823b5ecccea1c81def7f714a9a320e5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Sat, 20 Jun 2015 09:12:46 +0200 Subject: [PATCH 011/190] Fix import and deletion of projects --- taiga/events/events.py | 3 ++ taiga/projects/api.py | 44 ++++++++++++--------- taiga/projects/apps.py | 35 +++++++++++++---- taiga/projects/issues/apps.py | 48 +++++++++++++++-------- taiga/projects/tasks/apps.py | 62 +++++++++++++++++++----------- taiga/projects/userstories/apps.py | 49 ++++++++++++++++------- 6 files changed, 163 insertions(+), 78 deletions(-) diff --git a/taiga/events/events.py b/taiga/events/events.py index 8e32dcf7..26343694 100644 --- a/taiga/events/events.py +++ b/taiga/events/events.py @@ -53,6 +53,9 @@ def emit_event_for_model(obj, *, type:str="change", channel:str="events", Sends a model change event. """ + if obj._importing: + return None + assert type in set(["create", "change", "delete"]) assert hasattr(obj, "project_id") diff --git a/taiga/projects/api.py b/taiga/projects/api.py index e0761db2..975dd4ea 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -255,31 +255,37 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): super().pre_save(obj) def destroy(self, request, *args, **kwargs): + from taiga.events.apps import connect_events_signals, disconnect_events_signals + from taiga.projects.tasks.apps import connect_tasks_signals, disconnect_tasks_signals + from taiga.projects.userstories.apps import connect_userstories_signals, disconnect_userstories_signals + from taiga.projects.issues.apps import connect_issues_signals, disconnect_issues_signals + from taiga.projects.apps import connect_memberships_signals, disconnect_memberships_signals + obj = self.get_object_or_none() self.check_permissions(request, 'destroy', obj) - signals.post_delete.disconnect(sender=UserStory, - dispatch_uid="user_story_update_project_colors_on_delete") - signals.post_delete.disconnect(sender=Issue, - dispatch_uid="issue_update_project_colors_on_delete") - signals.post_delete.disconnect(sender=Task, - dispatch_uid="tasks_milestone_close_handler_on_delete") - signals.post_delete.disconnect(sender=Task, - dispatch_uid="tasks_us_close_handler_on_delete") - signals.post_delete.disconnect(sender=Task, - dispatch_uid="task_update_project_colors_on_delete") - signals.post_delete.disconnect(dispatch_uid="refprojdel") - signals.post_delete.disconnect(dispatch_uid='update_watchers_on_membership_post_delete') - - obj.tasks.all().delete() - obj.user_stories.all().delete() - obj.issues.all().delete() - obj.memberships.all().delete() - obj.roles.all().delete() - if obj is None: raise Http404 + disconnect_events_signals() + disconnect_issues_signals() + disconnect_tasks_signals() + disconnect_userstories_signals() + disconnect_memberships_signals() + + try: + obj.tasks.all().delete() + obj.user_stories.all().delete() + obj.issues.all().delete() + obj.memberships.all().delete() + obj.roles.all().delete() + finally: + connect_events_signals() + connect_issues_signals() + connect_tasks_signals() + connect_userstories_signals() + connect_memberships_signals() + self.pre_delete(obj) self.pre_conditions_on_delete(obj) obj.delete() diff --git a/taiga/projects/apps.py b/taiga/projects/apps.py index b59c0137..a57f2f5a 100644 --- a/taiga/projects/apps.py +++ b/taiga/projects/apps.py @@ -21,11 +21,7 @@ from django.db.models import signals from . import signals as handlers -class ProjectsAppConfig(AppConfig): - name = "taiga.projects" - verbose_name = "Projects" - - def ready(self): +def connect_memberships_signals(): # On membership object is deleted, update role-points relation. signals.pre_delete.connect(handlers.membership_post_delete, sender=apps.get_model("projects", "Membership"), @@ -41,6 +37,8 @@ class ProjectsAppConfig(AppConfig): sender=apps.get_model("projects", "Membership"), dispatch_uid='create-notify-policy') + +def connect_projects_signals(): # On project object is created apply template. signals.post_save.connect(handlers.project_post_save, sender=apps.get_model("projects", "Project"), @@ -48,6 +46,29 @@ class ProjectsAppConfig(AppConfig): # Tags signals.pre_save.connect(handlers.tags_normalization, - sender=apps.get_model("projects", "Project")) + sender=apps.get_model("projects", "Project"), + dispatch_uid="tags_normalization_projects") signals.pre_save.connect(handlers.update_project_tags_when_create_or_edit_taggable_item, - sender=apps.get_model("projects", "Project")) + sender=apps.get_model("projects", "Project"), + dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") + + +def disconnect_memberships_signals(): + signals.pre_delete.disconnect(dispatch_uid='membership_pre_delete') + signals.post_delete.disconnect(dispatch_uid='update_watchers_on_membership_post_delete') + signals.post_save.disconnect(dispatch_uid='create-notify-policy') + + +def disconnect_projects_signals(): + signals.post_save.disconnect(dispatch_uid='project_post_save') + signals.pre_save.disconnect(dispatch_uid="tags_normalization_projects") + signals.pre_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") + + +class ProjectsAppConfig(AppConfig): + name = "taiga.projects" + verbose_name = "Projects" + + def ready(self): + connect_memberships_signals() + connect_projects_signals() diff --git a/taiga/projects/issues/apps.py b/taiga/projects/issues/apps.py index 3972ef48..7333d934 100644 --- a/taiga/projects/issues/apps.py +++ b/taiga/projects/issues/apps.py @@ -23,25 +23,39 @@ from taiga.projects.custom_attributes import signals as custom_attributes_handle from . import signals as handlers +def connect_issues_signals(): + # Finished date + signals.pre_save.connect(handlers.set_finished_date_when_edit_issue, + sender=apps.get_model("issues", "Issue"), + dispatch_uid="set_finished_date_when_edit_issue") + + # Tags + signals.pre_save.connect(generic_handlers.tags_normalization, + sender=apps.get_model("issues", "Issue"), + dispatch_uid="tags_normalization_issue") + signals.post_save.connect(generic_handlers.update_project_tags_when_create_or_edit_taggable_item, + sender=apps.get_model("issues", "Issue"), + dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_issue") + signals.post_delete.connect(generic_handlers.update_project_tags_when_delete_taggable_item, + sender=apps.get_model("issues", "Issue"), + dispatch_uid="update_project_tags_when_delete_taggable_item_issue") + + # Custom Attributes + signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_issue, + sender=apps.get_model("issues", "Issue"), + dispatch_uid="create_custom_attribute_value_when_create_issue") + +def disconnect_issues_signals(): + signals.pre_save.disconnect(dispatch_uid="set_finished_date_when_edit_issue") + signals.pre_save.disconnect(dispatch_uid="tags_normalization_issue") + signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_issue") + signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_taggable_item_issue") + signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_issue") + + class IssuesAppConfig(AppConfig): name = "taiga.projects.issues" verbose_name = "Issues" def ready(self): - # Finished date - signals.pre_save.connect(handlers.set_finished_date_when_edit_issue, - sender=apps.get_model("issues", "Issue"), - dispatch_uid="set_finished_date_when_edit_issue") - - # Tags - signals.pre_save.connect(generic_handlers.tags_normalization, - sender=apps.get_model("issues", "Issue")) - signals.post_save.connect(generic_handlers.update_project_tags_when_create_or_edit_taggable_item, - sender=apps.get_model("issues", "Issue")) - signals.post_delete.connect(generic_handlers.update_project_tags_when_delete_taggable_item, - sender=apps.get_model("issues", "Issue")) - - # Custom Attributes - signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_issue, - sender=apps.get_model("issues", "Issue"), - dispatch_uid="create_custom_attribute_value_when_create_issue") + connect_issues_signals() diff --git a/taiga/projects/tasks/apps.py b/taiga/projects/tasks/apps.py index a6597339..f8426dcc 100644 --- a/taiga/projects/tasks/apps.py +++ b/taiga/projects/tasks/apps.py @@ -22,31 +22,49 @@ from taiga.projects import signals as generic_handlers from taiga.projects.custom_attributes import signals as custom_attributes_handlers from . import signals as handlers +def connect_tasks_signals(): + # Cached prev object version + signals.pre_save.connect(handlers.cached_prev_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="cached_prev_task") + + # Open/Close US and Milestone + signals.post_save.connect(handlers.try_to_close_or_open_us_and_milestone_when_create_or_edit_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") + signals.post_delete.connect(handlers.try_to_close_or_open_us_and_milestone_when_delete_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") + + # Tags + signals.pre_save.connect(generic_handlers.tags_normalization, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="tags_normalization_task") + signals.post_save.connect(generic_handlers.update_project_tags_when_create_or_edit_taggable_item, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="update_project_tags_when_create_or_edit_tagglabe_item_task") + signals.post_delete.connect(generic_handlers.update_project_tags_when_delete_taggable_item, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="update_project_tags_when_delete_tagglabe_item_task") + + # Custom Attributes + signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="create_custom_attribute_value_when_create_task") + +def disconnect_tasks_signals(): + signals.pre_save.disconnect(dispatch_uid="cached_prev_task") + signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") + signals.post_delete.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") + signals.pre_save.disconnect(dispatch_uid="tags_normalization") + signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_tagglabe_item") + signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_tagglabe_item") + signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_task") + class TasksAppConfig(AppConfig): name = "taiga.projects.tasks" verbose_name = "Tasks" def ready(self): - # Cached prev object version - signals.pre_save.connect(handlers.cached_prev_task, - sender=apps.get_model("tasks", "Task")) - - # Open/Close US and Milestone - signals.post_save.connect(handlers.try_to_close_or_open_us_and_milestone_when_create_or_edit_task, - sender=apps.get_model("tasks", "Task")) - signals.post_delete.connect(handlers.try_to_close_or_open_us_and_milestone_when_delete_task, - sender=apps.get_model("tasks", "Task")) - - # Tags - signals.pre_save.connect(generic_handlers.tags_normalization, - sender=apps.get_model("tasks", "Task")) - signals.post_save.connect(generic_handlers.update_project_tags_when_create_or_edit_taggable_item, - sender=apps.get_model("tasks", "Task")) - signals.post_delete.connect(generic_handlers.update_project_tags_when_delete_taggable_item, - sender=apps.get_model("tasks", "Task")) - - # Custom Attributes - signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_task, - sender=apps.get_model("tasks", "Task"), - dispatch_uid="create_custom_attribute_value_when_create_task") + connect_tasks_signals() diff --git a/taiga/projects/userstories/apps.py b/taiga/projects/userstories/apps.py index f6b1bb77..868074c1 100644 --- a/taiga/projects/userstories/apps.py +++ b/taiga/projects/userstories/apps.py @@ -23,38 +23,61 @@ from taiga.projects.custom_attributes import signals as custom_attributes_handle from . import signals as handlers -class UserStoriesAppConfig(AppConfig): - name = "taiga.projects.userstories" - verbose_name = "User Stories" - - def ready(self): +def connect_userstories_signals(): # Cached prev object version signals.pre_save.connect(handlers.cached_prev_us, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="cached_prev_us") # Role Points signals.post_save.connect(handlers.update_role_points_when_create_or_edit_us, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="update_role_points_when_create_or_edit_us") # Tasks signals.post_save.connect(handlers.update_milestone_of_tasks_when_edit_us, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="update_milestone_of_tasks_when_edit_us") # Open/Close US and Milestone signals.post_save.connect(handlers.try_to_close_or_open_us_and_milestone_when_create_or_edit_us, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_us") signals.post_delete.connect(handlers.try_to_close_milestone_when_delete_us, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="try_to_close_milestone_when_delete_us") # Tags signals.pre_save.connect(generic_handlers.tags_normalization, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="tags_normalization_user_story") signals.post_save.connect(generic_handlers.update_project_tags_when_create_or_edit_taggable_item, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_user_story") signals.post_delete.connect(generic_handlers.update_project_tags_when_delete_taggable_item, - sender=apps.get_model("userstories", "UserStory")) + sender=apps.get_model("userstories", "UserStory"), + dispatch_uid="update_project_tags_when_delete_taggable_item_user_story") # Custom Attributes signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_user_story, sender=apps.get_model("userstories", "UserStory"), dispatch_uid="create_custom_attribute_value_when_create_user_story") + +def disconnect_userstories_signals(): + signals.pre_save.disconnect(dispatch_uid="cached_prev_us") + signals.post_save.disconnect(dispatch_uid="update_role_points_when_create_or_edit_us") + signals.post_save.disconnect(dispatch_uid="update_milestone_of_tasks_when_edit_us") + signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_us") + signals.post_delete.disconnect(dispatch_uid="try_to_close_milestone_when_delete_us") + signals.pre_save.disconnect(dispatch_uid="tags_normalization_user_story") + signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_user_story") + signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_taggable_item_user_story") + signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_user_story") + + +class UserStoriesAppConfig(AppConfig): + name = "taiga.projects.userstories" + verbose_name = "User Stories" + + def ready(self): + connect_userstories_signals() From 9cbecd9b7edf82749761361454827575a0a19586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 18:33:11 +0200 Subject: [PATCH 012/190] Add support for partial words on global searches --- taiga/searches/services.py | 12 ++++++++---- tests/integration/test_searches.py | 20 +++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/taiga/searches/services.py b/taiga/searches/services.py index 6786df83..1cc2374e 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -25,9 +25,10 @@ def search_user_stories(project, text): model_cls = apps.get_model("userstories", "UserStory") where_clause = ("to_tsvector(coalesce(userstories_userstory.subject) || ' ' || " "coalesce(userstories_userstory.ref) || ' ' || " - "coalesce(userstories_userstory.description)) @@ plainto_tsquery(%s)") + "coalesce(userstories_userstory.description)) @@ to_tsquery(%s)") if text: + text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -38,9 +39,10 @@ def search_tasks(project, text): model_cls = apps.get_model("tasks", "Task") where_clause = ("to_tsvector(coalesce(tasks_task.subject, '') || ' ' || " "coalesce(tasks_task.ref) || ' ' || " - "coalesce(tasks_task.description, '')) @@ plainto_tsquery(%s)") + "coalesce(tasks_task.description, '')) @@ to_tsquery(%s)") if text: + text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -51,9 +53,10 @@ def search_issues(project, text): model_cls = apps.get_model("issues", "Issue") where_clause = ("to_tsvector(coalesce(issues_issue.subject) || ' ' || " "coalesce(issues_issue.ref) || ' ' || " - "coalesce(issues_issue.description)) @@ plainto_tsquery(%s)") + "coalesce(issues_issue.description)) @@ to_tsquery(%s)") if text: + text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -63,9 +66,10 @@ def search_issues(project, text): def search_wiki_pages(project, text): model_cls = apps.get_model("wiki", "WikiPage") where_clause = ("to_tsvector(coalesce(wiki_wikipage.slug) || ' ' || coalesce(wiki_wikipage.content)) " - "@@ plainto_tsquery(%s)") + "@@ to_tsquery(%s)") if text: + text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index 606f3304..0837c4c5 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -74,11 +74,11 @@ def searches_initial_data(): m.tsk2 = f.TaskFactory.create(project=m.project1) m.tsk3 = f.TaskFactory.create(project=m.project1, subject="Back to the future") - m.iss1 = f.IssueFactory.create(project=m.project1, subject="Backend and Frontend") + m.iss1 = f.IssueFactory.create(project=m.project1, subject="Design and Frontend") m.iss2 = f.IssueFactory.create(project=m.project2) - m.iss3 = f.IssueFactory.create(project=m.project1) + m.iss3 = f.IssueFactory.create(project=m.project1, subject="Green Frog") - m.wiki1 = f.WikiPageFactory.create(project=m.project1) + m.wiki1 = f.WikiPageFactory.create(project=m.project1, content="Final Frontier") m.wiki2 = f.WikiPageFactory.create(project=m.project1, content="Frontend, future") m.wiki3 = f.WikiPageFactory.create(project=m.project2) @@ -131,6 +131,20 @@ def test_search_text_query_in_my_project(client, searches_initial_data): assert len(response.data["wikipages"]) == 0 +def test_search_partial_text_query_in_my_project(client, searches_initial_data): + data = searches_initial_data + + client.login(data.member1.user) + + response = client.get(reverse("search-list"), {"project": data.project1.id, "text": "fron"}) + assert response.status_code == 200 + assert response.data["count"] == 3 + assert len(response.data["userstories"]) == 0 + assert len(response.data["tasks"]) == 0 + assert len(response.data["issues"]) == 1 + assert len(response.data["wikipages"]) == 2 + + def test_search_text_query_with_an_invalid_project_id(client, searches_initial_data): data = searches_initial_data From 79c079d2cb7c8d455a5a4015c455d3d2e5b12d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 19:22:47 +0200 Subject: [PATCH 013/190] Allow to include id attributes in the elements (to allow anchor links) --- taiga/mdrender/service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/mdrender/service.py b/taiga/mdrender/service.py index 5b9207c8..077c0c63 100644 --- a/taiga/mdrender/service.py +++ b/taiga/mdrender/service.py @@ -61,7 +61,7 @@ bleach.ALLOWED_STYLES.append("background") bleach.ALLOWED_ATTRIBUTES["a"] = ["href", "title", "alt", "target"] bleach.ALLOWED_ATTRIBUTES["img"] = ["alt", "src"] -bleach.ALLOWED_ATTRIBUTES["*"] = ["class", "style"] +bleach.ALLOWED_ATTRIBUTES["*"] = ["class", "style", "id"] def _make_extensions_list(project=None): From 9f253394a455731f57438933886ee670616683fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 24 Jun 2015 00:41:32 +0200 Subject: [PATCH 014/190] Refactored some tets --- tests/integration/test_exporter_api.py | 4 +- tests/integration/test_hooks_bitbucket.py | 4 +- tests/integration/test_hooks_github.py | 4 +- tests/integration/test_hooks_gitlab.py | 4 +- tests/integration/test_importer_api.py | 64 +++++++++++------------ tests/integration/test_notifications.py | 2 +- tests/integration/test_occ.py | 24 ++++----- tests/integration/test_projects.py | 8 +-- tests/integration/test_users.py | 8 +-- tests/integration/test_userstories.py | 8 +-- 10 files changed, 65 insertions(+), 65 deletions(-) diff --git a/tests/integration/test_exporter_api.py b/tests/integration/test_exporter_api.py index 7758fdf6..dee2f5bb 100644 --- a/tests/integration/test_exporter_api.py +++ b/tests/integration/test_exporter_api.py @@ -47,7 +47,7 @@ def test_valid_project_export_with_celery_disabled(client, settings): response = client.get(url, content_type="application/json") assert response.status_code == 200 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert "url" in response_data @@ -63,7 +63,7 @@ def test_valid_project_export_with_celery_enabled(client, settings): response = client.get(url, content_type="application/json") assert response.status_code == 202 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert "export_id" in response_data diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index 4cf3bab8..c8a8670c 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -32,7 +32,7 @@ def test_bad_signature(client): url = "{}?project={}&key={}".format(url, project.id, "badbadbad") data = {} response = client.post(url, urllib.parse.urlencode(data, True), content_type="application/x-www-form-urlencoded") - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert response.status_code == 400 assert "Bad signature" in response_content["_error_message"] @@ -232,7 +232,7 @@ def test_api_get_project_modules(client): client.login(project.owner) response = client.get(url) assert response.status_code == 200 - content = json.loads(response.content.decode("utf-8")) + content = response.data assert "bitbucket" in content assert content["bitbucket"]["secret"] != "" assert content["bitbucket"]["webhooks_url"] != "" diff --git a/tests/integration/test_hooks_github.py b/tests/integration/test_hooks_github.py index d9861f89..39994f71 100644 --- a/tests/integration/test_hooks_github.py +++ b/tests/integration/test_hooks_github.py @@ -30,7 +30,7 @@ def test_bad_signature(client): response = client.post(url, json.dumps(data), HTTP_X_HUB_SIGNATURE="sha1=badbadbad", content_type="application/json") - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert response.status_code == 400 assert "Bad signature" in response_content["_error_message"] @@ -421,7 +421,7 @@ def test_api_get_project_modules(client): client.login(project.owner) response = client.get(url) assert response.status_code == 200 - content = json.loads(response.content.decode("utf-8")) + content = response.data assert "github" in content assert content["github"]["secret"] != "" assert content["github"]["webhooks_url"] != "" diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index 10935c46..64c4765c 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -33,7 +33,7 @@ def test_bad_signature(client): url = "{}?project={}&key={}".format(url, project.id, "badbadbad") data = {} response = client.post(url, json.dumps(data), content_type="application/json") - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert response.status_code == 400 assert "Bad signature" in response_content["_error_message"] @@ -349,7 +349,7 @@ def test_api_get_project_modules(client): client.login(project.owner) response = client.get(url) assert response.status_code == 200 - content = json.loads(response.content.decode("utf-8")) + content = response.data assert "gitlab" in content assert content["gitlab"]["secret"] != "" assert content["gitlab"]["webhooks_url"] != "" diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index 94b72561..96143112 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -58,7 +58,7 @@ def test_valid_project_import_without_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data must_empty_children = [ "issues", "user_stories", "us_statuses", "wiki_pages", "priorities", "severities", "milestones", "points", "issue_types", "task_statuses", @@ -85,7 +85,7 @@ def test_valid_project_import_with_not_existing_memberships(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data # The new membership and the owner membership assert len(response_data["memberships"]) == 2 @@ -108,7 +108,7 @@ def test_valid_project_import_with_membership_uuid_rewrite(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert Membership.objects.filter(email="with-uuid@email.com", token="123").count() == 0 @@ -149,7 +149,7 @@ def test_valid_project_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data must_empty_children = [ "issues", "user_stories", "wiki_pages", "milestones", "wiki_links", @@ -178,7 +178,7 @@ def test_invalid_project_import_without_roles(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 2 assert Project.objects.filter(slug="imported-project").count() == 0 @@ -205,7 +205,7 @@ def test_invalid_project_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 7 assert Project.objects.filter(slug="imported-project").count() == 0 @@ -302,7 +302,7 @@ def test_valid_user_story_import(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["subject"] == "Imported issue" assert response_data["finish_date"] == "2014-10-24T00:00:00+0000" @@ -349,7 +349,7 @@ def test_valid_issue_import_without_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -408,7 +408,7 @@ def test_valid_issue_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -435,7 +435,7 @@ def test_invalid_issue_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 assert Issue.objects.filter(subject="Imported issue").count() == 0 @@ -460,7 +460,7 @@ def test_invalid_issue_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 url = reverse("importer-issue", args=[project.pk]) @@ -472,7 +472,7 @@ def test_invalid_issue_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 url = reverse("importer-issue", args=[project.pk]) @@ -484,7 +484,7 @@ def test_invalid_issue_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 url = reverse("importer-issue", args=[project.pk]) @@ -496,7 +496,7 @@ def test_invalid_issue_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 @@ -528,7 +528,7 @@ def test_valid_us_import_without_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -556,7 +556,7 @@ def test_valid_us_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -579,7 +579,7 @@ def test_invalid_us_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 assert UserStory.objects.filter(subject="Imported us").count() == 0 @@ -601,7 +601,7 @@ def test_invalid_us_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 @@ -633,7 +633,7 @@ def test_valid_task_import_without_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -685,7 +685,7 @@ def test_valid_task_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email assert response_data["ref"] is not None @@ -708,7 +708,7 @@ def test_invalid_task_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 assert Task.objects.filter(subject="Imported task").count() == 0 @@ -730,7 +730,7 @@ def test_invalid_task_import_with_bad_choices(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 @@ -781,7 +781,7 @@ def test_valid_wiki_page_import_without_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["owner"] == user.email @@ -806,7 +806,7 @@ def test_valid_wiki_page_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email @@ -826,7 +826,7 @@ def test_invalid_wiki_page_import_with_extra_data(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert len(response_data) == 1 assert WikiPage.objects.filter(slug="imported-wiki-page").count() == 0 @@ -858,7 +858,7 @@ def test_valid_wiki_link_import(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - json.loads(response.content.decode("utf-8")) + response.data @@ -890,7 +890,7 @@ def test_valid_milestone_import(client): response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - json.loads(response.content.decode("utf-8")) + response.data @@ -910,7 +910,7 @@ def test_milestone_import_duplicated_milestone(client): response = client.post(url, json.dumps(data), content_type="application/json") response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["milestones"][0]["name"][0] == "Name duplicated for the project" @@ -925,7 +925,7 @@ def test_invalid_dump_import(client): response = client.post(url, {'dump': data}) assert response.status_code == 400 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["_error_message"] == "Invalid dump format" @@ -946,7 +946,7 @@ def test_valid_dump_import_with_celery_disabled(client, settings): response = client.post(url, {'dump': data}) assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert "id" in response_data assert response_data["name"] == "Valid project" @@ -968,7 +968,7 @@ def test_valid_dump_import_with_celery_enabled(client, settings): response = client.post(url, {'dump': data}) assert response.status_code == 202 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert "import_id" in response_data @@ -988,7 +988,7 @@ def test_dump_import_duplicated_project(client): response = client.post(url, {'dump': data}) assert response.status_code == 201 - response_data = json.loads(response.content.decode("utf-8")) + response_data = response.data assert response_data["name"] == "Test import" assert response_data["slug"] == "{}-test-import".format(user.username) diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 7da6469d..d3d64ddb 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -463,4 +463,4 @@ def test_retrieve_notify_policies_by_anonymous_user(client): url = reverse("notifications-detail", args=[policy.pk]) response = client.get(url, content_type="application/json") assert response.status_code == 404, response.status_code - assert json.loads(response.content.decode("utf-8"))["_error_message"] == "No NotifyPolicy matches the given query.", response.content + assert response.data["_error_message"] == "No NotifyPolicy matches the given query.", response.content diff --git a/tests/integration/test_occ.py b/tests/integration/test_occ.py index 9826cf0e..2244750d 100644 --- a/tests/integration/test_occ.py +++ b/tests/integration/test_occ.py @@ -61,7 +61,7 @@ def test_invalid_concurrent_save_for_issue(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201, response.content - issue_id = json.loads(response.content)["id"] + issue_id = response.data["id"] url = reverse("issues-detail", args=(issue_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -90,7 +90,7 @@ def test_valid_concurrent_save_for_issue_different_versions(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201, response.content - issue_id = json.loads(response.content)["id"] + issue_id = response.data["id"] url = reverse("issues-detail", args=(issue_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -119,7 +119,7 @@ def test_valid_concurrent_save_for_issue_different_fields(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201, response.content - issue_id = json.loads(response.content)["id"] + issue_id = response.data["id"] url = reverse("issues-detail", args=(issue_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -143,7 +143,7 @@ def test_invalid_concurrent_save_for_wiki_page(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201, response.content - wiki_id = json.loads(response.content)["id"] + wiki_id = response.data["id"] url = reverse("wiki-detail", args=(wiki_id,)) data = {"version": 1, "content": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -167,7 +167,7 @@ def test_valid_concurrent_save_for_wiki_page_different_versions(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201, response.content - wiki_id = json.loads(response.content)["id"] + wiki_id = response.data["id"] url = reverse("wiki-detail", args=(wiki_id,)) data = {"version": 1, "content": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -194,7 +194,7 @@ def test_invalid_concurrent_save_for_us(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - userstory_id = json.loads(response.content)["id"] + userstory_id = response.data["id"] url = reverse("userstories-detail", args=(userstory_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -220,7 +220,7 @@ def test_valid_concurrent_save_for_us_different_versions(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - userstory_id = json.loads(response.content)["id"] + userstory_id = response.data["id"] url = reverse("userstories-detail", args=(userstory_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -246,7 +246,7 @@ def test_valid_concurrent_save_for_us_different_fields(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - userstory_id = json.loads(response.content)["id"] + userstory_id = response.data["id"] url = reverse("userstories-detail", args=(userstory_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -272,7 +272,7 @@ def test_invalid_concurrent_save_for_task(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - task_id = json.loads(response.content)["id"] + task_id = response.data["id"] url = reverse("tasks-detail", args=(task_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -298,7 +298,7 @@ def test_valid_concurrent_save_for_task_different_versions(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - task_id = json.loads(response.content)["id"] + task_id = response.data["id"] url = reverse("tasks-detail", args=(task_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -324,7 +324,7 @@ def test_valid_concurrent_save_for_task_different_fields(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - task_id = json.loads(response.content)["id"] + task_id = response.data["id"] url = reverse("tasks-detail", args=(task_id,)) data = {"version": 1, "subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") @@ -351,7 +351,7 @@ def test_invalid_save_without_version_parameter(client): response = client.json.post(url, json.dumps(data)) assert response.status_code == 201 - task_id = json.loads(response.content)["id"] + task_id = response.data["id"] url = reverse("tasks-detail", args=(task_id,)) data = {"subject": "test 1"} response = client.patch(url, json.dumps(data), content_type="application/json") diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 44de3056..a539dd09 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -200,7 +200,7 @@ def test_leave_project_valid_membership_only_owner(client): url = reverse("projects-leave", args=(project.id,)) response = client.post(url) assert response.status_code == 403 - assert json.loads(response.content)["_error_message"] == "You can't leave the project if there are no more owners" + assert response.data["_error_message"] == "You can't leave the project if there are no more owners" def test_leave_project_invalid_membership(client): @@ -221,7 +221,7 @@ def test_delete_membership_only_owner(client): url = reverse("memberships-detail", args=(membership.id,)) response = client.delete(url) assert response.status_code == 400 - assert json.loads(response.content)["_error_message"] == "At least one of the user must be an active admin" + assert response.data["_error_message"] == "At least one of the user must be an active admin" def test_edit_membership_only_owner(client): @@ -339,7 +339,7 @@ def test_projects_user_order(client): url = reverse("projects-list") url = "%s?member=%s" % (url, user.id) response = client.json.get(url) - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert response.status_code == 200 assert(response_content[0]["id"] == project_1.id) @@ -347,6 +347,6 @@ def test_projects_user_order(client): url = reverse("projects-list") url = "%s?member=%s&order_by=memberships__user_order" % (url, user.id) response = client.json.get(url) - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert response.status_code == 200 assert(response_content[0]["id"] == project_2.id) diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 1898aea8..2d3f1664 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -202,13 +202,13 @@ def test_list_contacts_private_projects(client): url = reverse('users-contacts', kwargs={"pk": user_1.pk}) response = client.get(url, content_type="application/json") assert response.status_code == 200 - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert len(response_content) == 0 client.login(user_1) response = client.get(url, content_type="application/json") assert response.status_code == 200 - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert len(response_content) == 1 assert response_content[0]["id"] == user_2.id @@ -227,7 +227,7 @@ def test_list_contacts_no_projects(client): response = client.get(url, content_type="application/json") assert response.status_code == 200 - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert len(response_content) == 0 @@ -246,6 +246,6 @@ def test_list_contacts_public_projects(client): response = client.get(url, content_type="application/json") assert response.status_code == 200 - response_content = json.loads(response.content.decode("utf-8")) + response_content = response.data assert len(response_content) == 1 assert response_content[0]["id"] == user_2.id diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 9819d413..91bee3b0 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -203,15 +203,15 @@ def test_archived_filter(client): data = {} response = client.get(url, data) - assert len(json.loads(response.content)) == 2 + assert len(response.data) == 2 data = {"status__is_archived": 0} response = client.get(url, data) - assert len(json.loads(response.content)) == 1 + assert len(response.data) == 1 data = {"status__is_archived": 1} response = client.get(url, data) - assert len(json.loads(response.content)) == 1 + assert len(response.data) == 1 def test_filter_by_multiple_status(client): @@ -230,7 +230,7 @@ def test_filter_by_multiple_status(client): data = {} response = client.get(url, data) - assert len(json.loads(response.content)) == 2 + assert len(response.data) == 2 def test_get_total_points(client): From 4ae37167f5dd13c88bcc0bdfa69a7b755220cd50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 18:08:09 +0200 Subject: [PATCH 015/190] Allow multiple message actions on commit --- CHANGELOG.md | 1 + taiga/hooks/bitbucket/event_hooks.py | 3 +-- taiga/hooks/github/event_hooks.py | 3 +-- taiga/hooks/gitlab/event_hooks.py | 3 +-- tests/integration/test_hooks_bitbucket.py | 20 +++++++++++++++++++ tests/integration/test_hooks_github.py | 24 +++++++++++++++++++++++ tests/integration/test_hooks_gitlab.py | 24 +++++++++++++++++++++++ 7 files changed, 72 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ec53387..68349c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) ### Misc +- Allow multiple actions in the commit messages. - Lots of small and not so small bugfixes. diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 7461eabb..969dae64 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -61,8 +61,7 @@ class PushEventHook(BaseEventHook): return p = re.compile("tg-(\d+) +#([-\w]+)") - m = p.search(message.lower()) - if m: + for m in p.finditer(message.lower()): ref = m.group(1) status_slug = m.group(2) self._change_status(ref, status_slug, bitbucket_user) diff --git a/taiga/hooks/github/event_hooks.py b/taiga/hooks/github/event_hooks.py index e34c1a2f..3dbd0417 100644 --- a/taiga/hooks/github/event_hooks.py +++ b/taiga/hooks/github/event_hooks.py @@ -56,8 +56,7 @@ class PushEventHook(BaseEventHook): return p = re.compile("tg-(\d+) +#([-\w]+)") - m = p.search(message.lower()) - if m: + for m in p.finditer(message.lower()): ref = m.group(1) status_slug = m.group(2) self._change_status(ref, status_slug, github_user, commit) diff --git a/taiga/hooks/gitlab/event_hooks.py b/taiga/hooks/gitlab/event_hooks.py index 8776d8c7..84079121 100644 --- a/taiga/hooks/gitlab/event_hooks.py +++ b/taiga/hooks/gitlab/event_hooks.py @@ -54,8 +54,7 @@ class PushEventHook(BaseEventHook): return p = re.compile("tg-(\d+) +#([-\w]+)") - m = p.search(message.lower()) - if m: + for m in p.finditer(message.lower()): ref = m.group(1) status_slug = m.group(2) self._change_status(ref, status_slug, gitlab_user) diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index 4cf3bab8..83a67d3a 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -160,6 +160,26 @@ def test_push_event_user_story_processing(client): assert len(mail.outbox) == 1 +def test_push_event_multiple_actions(client): + creation_status = f.IssueStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.IssueStatusFactory(project=creation_status.project) + issue1 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + issue2 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = [ + '{"commits": [{"message": "test message test TG-%s #%s ok test TG-%s #%s ok bye!"}]}' % (issue1.ref, new_status.slug, issue2.ref, new_status.slug) + ] + mail.outbox = [] + ev_hook1 = event_hooks.PushEventHook(issue1.project, payload) + ev_hook1.process_event() + issue1 = Issue.objects.get(id=issue1.id) + issue2 = Issue.objects.get(id=issue2.id) + assert issue1.status.id == new_status.id + assert issue2.status.id == new_status.id + assert len(mail.outbox) == 2 + + def test_push_event_processing_case_insensitive(client): creation_status = f.TaskStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"]) diff --git a/tests/integration/test_hooks_github.py b/tests/integration/test_hooks_github.py index d9861f89..43b092f5 100644 --- a/tests/integration/test_hooks_github.py +++ b/tests/integration/test_hooks_github.py @@ -134,6 +134,30 @@ def test_push_event_user_story_processing(client): assert len(mail.outbox) == 1 +def test_push_event_multiple_actions(client): + creation_status = f.IssueStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.IssueStatusFactory(project=creation_status.project) + issue1 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + issue2 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = {"commits": [ + {"message": """test message + test TG-%s #%s ok + test TG-%s #%s ok + bye! + """ % (issue1.ref, new_status.slug, issue2.ref, new_status.slug)}, + ]} + mail.outbox = [] + ev_hook1 = event_hooks.PushEventHook(issue1.project, payload) + ev_hook1.process_event() + issue1 = Issue.objects.get(id=issue1.id) + issue2 = Issue.objects.get(id=issue2.id) + assert issue1.status.id == new_status.id + assert issue2.status.id == new_status.id + assert len(mail.outbox) == 2 + + def test_push_event_processing_case_insensitive(client): creation_status = f.TaskStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"]) diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index 10935c46..fc02a4c2 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -179,6 +179,30 @@ def test_push_event_user_story_processing(client): assert len(mail.outbox) == 1 +def test_push_event_multiple_actions(client): + creation_status = f.IssueStatusFactory() + role = f.RoleFactory(project=creation_status.project, permissions=["view_issues"]) + f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) + new_status = f.IssueStatusFactory(project=creation_status.project) + issue1 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + issue2 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) + payload = {"commits": [ + {"message": """test message + test TG-%s #%s ok + test TG-%s #%s ok + bye! + """ % (issue1.ref, new_status.slug, issue2.ref, new_status.slug)}, + ]} + mail.outbox = [] + ev_hook1 = event_hooks.PushEventHook(issue1.project, payload) + ev_hook1.process_event() + issue1 = Issue.objects.get(id=issue1.id) + issue2 = Issue.objects.get(id=issue2.id) + assert issue1.status.id == new_status.id + assert issue2.status.id == new_status.id + assert len(mail.outbox) == 2 + + def test_push_event_processing_case_insensitive(client): creation_status = f.TaskStatusFactory() role = f.RoleFactory(project=creation_status.project, permissions=["view_tasks"]) From c32d505b96fb875d7da421246f793e9b23a5f976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 21:23:54 +0200 Subject: [PATCH 016/190] Allow default status, priority, severity and type in issues and tasks --- .../migrations/0005_auto_20150623_1923.py | 38 +++++++++++++++++++ taiga/projects/issues/models.py | 8 ++-- .../migrations/0006_auto_20150623_1923.py | 20 ++++++++++ taiga/projects/tasks/models.py | 2 +- tests/integration/test_issues.py | 25 ++++++++++++ tests/integration/test_tasks.py | 17 +++++++++ tests/integration/test_userstories.py | 17 +++++++++ 7 files changed, 122 insertions(+), 5 deletions(-) create mode 100644 taiga/projects/issues/migrations/0005_auto_20150623_1923.py create mode 100644 taiga/projects/tasks/migrations/0006_auto_20150623_1923.py diff --git a/taiga/projects/issues/migrations/0005_auto_20150623_1923.py b/taiga/projects/issues/migrations/0005_auto_20150623_1923.py new file mode 100644 index 00000000..f97057cd --- /dev/null +++ b/taiga/projects/issues/migrations/0005_auto_20150623_1923.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('issues', '0004_auto_20150114_0954'), + ] + + operations = [ + migrations.AlterField( + model_name='issue', + name='priority', + field=models.ForeignKey(blank=True, null=True, to='projects.Priority', related_name='issues', verbose_name='priority'), + preserve_default=True, + ), + migrations.AlterField( + model_name='issue', + name='severity', + field=models.ForeignKey(blank=True, null=True, to='projects.Severity', related_name='issues', verbose_name='severity'), + preserve_default=True, + ), + migrations.AlterField( + model_name='issue', + name='status', + field=models.ForeignKey(blank=True, null=True, to='projects.IssueStatus', related_name='issues', verbose_name='status'), + preserve_default=True, + ), + migrations.AlterField( + model_name='issue', + name='type', + field=models.ForeignKey(blank=True, null=True, to='projects.IssueType', related_name='issues', verbose_name='type'), + preserve_default=True, + ), + ] diff --git a/taiga/projects/issues/models.py b/taiga/projects/issues/models.py index 397e03db..943509b3 100644 --- a/taiga/projects/issues/models.py +++ b/taiga/projects/issues/models.py @@ -36,13 +36,13 @@ class Issue(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models. verbose_name=_("ref")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name="owned_issues", verbose_name=_("owner")) - status = models.ForeignKey("projects.IssueStatus", null=False, blank=False, + status = models.ForeignKey("projects.IssueStatus", null=True, blank=True, related_name="issues", verbose_name=_("status")) - severity = models.ForeignKey("projects.Severity", null=False, blank=False, + severity = models.ForeignKey("projects.Severity", null=True, blank=True, related_name="issues", verbose_name=_("severity")) - priority = models.ForeignKey("projects.Priority", null=False, blank=False, + priority = models.ForeignKey("projects.Priority", null=True, blank=True, related_name="issues", verbose_name=_("priority")) - type = models.ForeignKey("projects.IssueType", null=False, blank=False, + type = models.ForeignKey("projects.IssueType", null=True, blank=True, related_name="issues", verbose_name=_("type")) milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True, default=None, related_name="issues", diff --git a/taiga/projects/tasks/migrations/0006_auto_20150623_1923.py b/taiga/projects/tasks/migrations/0006_auto_20150623_1923.py new file mode 100644 index 00000000..077fefe8 --- /dev/null +++ b/taiga/projects/tasks/migrations/0006_auto_20150623_1923.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0005_auto_20150114_0954'), + ] + + operations = [ + migrations.AlterField( + model_name='task', + name='status', + field=models.ForeignKey(blank=True, null=True, to='projects.TaskStatus', related_name='tasks', verbose_name='status'), + preserve_default=True, + ), + ] diff --git a/taiga/projects/tasks/models.py b/taiga/projects/tasks/models.py index c35de144..699321c0 100644 --- a/taiga/projects/tasks/models.py +++ b/taiga/projects/tasks/models.py @@ -35,7 +35,7 @@ class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M verbose_name=_("ref")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, default=None, related_name="owned_tasks", verbose_name=_("owner")) - status = models.ForeignKey("projects.TaskStatus", null=False, blank=False, + status = models.ForeignKey("projects.TaskStatus", null=True, blank=True, related_name="tasks", verbose_name=_("status")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="tasks", verbose_name=_("project")) diff --git a/tests/integration/test_issues.py b/tests/integration/test_issues.py index 43ac01e0..a3aa9db0 100644 --- a/tests/integration/test_issues.py +++ b/tests/integration/test_issues.py @@ -46,6 +46,31 @@ def test_update_issues_order_in_bulk(): model=models.Issue) +def test_create_issue_without_status(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + status = f.IssueStatusFactory.create(project=project) + priority = f.PriorityFactory.create(project=project) + severity = f.SeverityFactory.create(project=project) + type = f.IssueTypeFactory.create(project=project) + project.default_issue_status = status + project.default_priority = priority + project.default_severity = severity + project.default_issue_type = type + project.save() + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("issues-list") + + data = {"subject": "Test user story", "project": project.id} + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 201 + assert response.data['status'] == project.default_issue_status.id + assert response.data['severity'] == project.default_severity.id + assert response.data['priority'] == project.default_priority.id + assert response.data['type'] == project.default_issue_type.id + + def test_api_create_issues_in_bulk(client): project = f.create_project() f.MembershipFactory(project=project, user=project.owner, is_owner=True) diff --git a/tests/integration/test_tasks.py b/tests/integration/test_tasks.py index 6f8d3206..61d1954a 100644 --- a/tests/integration/test_tasks.py +++ b/tests/integration/test_tasks.py @@ -36,6 +36,23 @@ Task #2 db.save_in_bulk.assert_called_once_with(tasks, None, None) +def test_create_task_without_status(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + status = f.TaskStatusFactory.create(project=project) + project.default_task_status = status + project.save() + + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("tasks-list") + + data = {"subject": "Test user story", "project": project.id} + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 201 + assert response.data['status'] == project.default_task_status.id + + def test_api_update_task_tags(client): task = f.create_task() f.MembershipFactory.create(project=task.project, user=task.owner, is_owner=True) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 91bee3b0..e86f1dad 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -45,6 +45,23 @@ def test_update_userstories_order_in_bulk(): model=models.UserStory) +def test_create_userstory_without_status(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + status = f.UserStoryStatusFactory.create(project=project) + project.default_us_status = status + project.save() + + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("userstories-list") + + data = {"subject": "Test user story", "project": project.id} + client.login(user) + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 201 + assert response.data['status'] == project.default_us_status.id + + def test_api_delete_userstory(client): us = f.UserStoryFactory.create() f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) From d70800d3e66a212c9f1c84d689d06947dd2b26ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 23 Jun 2015 17:14:07 +0200 Subject: [PATCH 017/190] US #2911: Mixin 'users', 'members' and 'memberships' in ProjectDetailSerializer --- CHANGELOG.md | 4 +++- taiga/projects/serializers.py | 39 ++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68349c38..2b5fdcda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,13 @@ ## 1.9.0 ??? (unreleased) + ### Features - Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) +- Allow multiple actions in the commit messages. ### Misc -- Allow multiple actions in the commit messages. +- API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer - Lots of small and not so small bugfixes. diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 5ca3e7f4..d34d5939 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -272,21 +272,6 @@ class MembershipAdminSerializer(MembershipSerializer): exclude = ("token",) -class ProjectMembershipSerializer(serializers.ModelSerializer): - role_name = serializers.CharField(source='role.name', required=False, i18n=True) - full_name = serializers.CharField(source='user.get_full_name', required=False) - username = serializers.CharField(source='user.username', required=False) - color = serializers.CharField(source='user.color', required=False) - is_active = serializers.BooleanField(source='user.is_active', required=False) - photo = serializers.SerializerMethodField("get_photo") - - class Meta: - model = models.Membership - - def get_photo(self, project): - return get_photo_or_gravatar_url(project.user) - - class MemberBulkSerializer(RoleExistsValidator, serializers.Serializer): email = serializers.EmailField() role_id = serializers.IntegerField() @@ -298,6 +283,23 @@ class MembersBulkSerializer(ProjectExistsValidator, serializers.Serializer): invitation_extra_text = serializers.CharField(required=False, max_length=255) +class ProjectMemberSerializer(serializers.ModelSerializer): + id = serializers.IntegerField(source="user.id", read_only=True) + username = serializers.CharField(source='user.username', read_only=True) + full_name = serializers.CharField(source='user.full_name', read_only=True) + full_name_display = serializers.CharField(source='user.get_full_name', read_only=True) + color = serializers.CharField(source='user.color', read_only=True) + photo = serializers.SerializerMethodField("get_photo") + is_active = serializers.BooleanField(source='user.is_active', read_only=True) + role_name = serializers.CharField(source='role.name', read_only=True, i18n=True) + + class Meta: + model = models.Membership + + def get_photo(self, membership): + return get_photo_or_gravatar_url(membership.user) + + ###################################################### ## Projects ###################################################### @@ -365,15 +367,14 @@ class ProjectDetailSerializer(ProjectSerializer): many=True, required=False) roles = ProjectRoleSerializer(source="roles", many=True, read_only=True) - users = UserSerializer(source="members", many=True, read_only=True) - memberships = serializers.SerializerMethodField(method_name="get_memberships") + members = serializers.SerializerMethodField(method_name="get_members") - def get_memberships(self, obj): + def get_members(self, obj): qs = obj.memberships.filter(user__isnull=False) qs = qs.extra(select={"complete_user_name":"concat(full_name, username)"}) qs = qs.order_by("complete_user_name") qs = qs.select_related("role", "user") - serializer = ProjectMembershipSerializer(qs, many=True) + serializer = ProjectMemberSerializer(qs, many=True) return serializer.data From 6a0b4687d8e908006124ec5784c91553b1b00758 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 23 Jun 2015 12:49:36 +0200 Subject: [PATCH 018/190] Issue #2916: When leaving a project I lose all my watched stuff (even from different projects) --- taiga/projects/signals.py | 9 ++++++++- tests/integration/test_projects.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/taiga/projects/signals.py b/taiga/projects/signals.py index e4366d8d..61dd0709 100644 --- a/taiga/projects/signals.py +++ b/taiga/projects/signals.py @@ -19,6 +19,7 @@ from django.conf import settings from taiga.projects.services.tags_colors import update_project_tags_colors_handler, remove_unused_tags from taiga.projects.notifications.services import create_notify_policy_if_not_exists +from taiga.base.utils.db import get_typename_for_model_class #################################### @@ -53,7 +54,13 @@ def update_watchers_on_membership_post_delete(sender, instance, using, **kwargs) # instance.user can contain pointer to now # removed object from a database. for model in models: - model.watchers.through.objects.filter(user_id=instance.user_id).delete() + #filter(project=instance.project) + filter = { + "user_id": instance.user_id, + "%s__project"%(model._meta.model_name): instance.project, + } + + model.watchers.through.objects.filter(**filter).delete() def create_notify_policy(sender, instance, using, **kwargs): diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index a539dd09..527b0d0e 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -212,6 +212,22 @@ def test_leave_project_invalid_membership(client): assert response.status_code == 404 +def test_leave_project_respect_watching_items(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=["view_project"]) + f.MembershipFactory.create(project=project, user=user, role=role) + issue = f.IssueFactory(owner=user) + issue.watchers=[user] + issue.save() + + client.login(user) + url = reverse("projects-leave", args=(project.id,)) + response = client.post(url) + assert response.status_code == 200 + assert list(issue.watchers.all()) == [user] + + def test_delete_membership_only_owner(client): user = f.UserFactory.create() project = f.ProjectFactory.create() From 816743982879b1681c6f3dbd3373823f31e99d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 24 Jun 2015 13:31:46 +0200 Subject: [PATCH 019/190] [i18n] Update locales --- taiga/locale/de/LC_MESSAGES/django.po | 138 +++++++++++++++++++++----- 1 file changed, 115 insertions(+), 23 deletions(-) diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index cf2bc68f..9147444a 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-20 14:15+0000\n" -"Last-Translator: Hans Raaf\n" +"PO-Revision-Date: 2015-06-24 10:12+0000\n" +"Last-Translator: Regina \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -65,7 +65,7 @@ msgstr "Der Benutzer ist schon registriert." #: taiga/auth/services.py:146 msgid "Membership with user is already exists." -msgstr "Der Benutzer für diese Mitgliedschaft, existiert bereits." +msgstr "Der Benutzer für diese Mitgliedschaft existiert bereits." #: taiga/auth/services.py:172 msgid "Error on creating new user." @@ -265,7 +265,7 @@ msgstr "Es gab keine Eingabe" #: taiga/base/api/serializers.py:548 msgid "Cannot create a new item, only existing items may be updated." msgstr "" -"Es können nur existierende Einträge aktualisiertwerden. Eine Neuerstellung " +"Es können nur existierende Einträge aktualisiert werden. Eine Neuerstellung " "ist nicht möglich." #: taiga/base/api/serializers.py:559 @@ -337,7 +337,7 @@ msgstr "Nicht gefunden." #: taiga/base/exceptions.py:130 msgid "Method not supported for this endpoint." -msgstr "" +msgstr "Methode wird für diesen Endpunkt nicht unterstützt. " #: taiga/base/exceptions.py:138 taiga/base/exceptions.py:146 msgid "Wrong arguments." @@ -357,7 +357,7 @@ msgstr "Voraussetzungsfehler" #: taiga/base/filters.py:74 msgid "Error in filter params types." -msgstr "" +msgstr "Fehler in Filter Parameter Typen." #: taiga/base/filters.py:121 taiga/base/filters.py:210 #: taiga/base/filters.py:259 @@ -470,6 +470,10 @@ msgid "" "%(comment)s

\n" " " msgstr "" +"\n" +"

Kommentar:

\n" +"

%(comment)s

\n" +" " #: taiga/base/templates/emails/updates-body-text.jinja:6 #, python-format @@ -536,7 +540,7 @@ msgstr "Fehler beim Importieren der Tickets" #: taiga/export_import/dump_service.py:169 msgid "error importing user stories" -msgstr "Fehler beim Importieren der User Stories" +msgstr "Fehler beim Importieren der User-Stories" #: taiga/export_import/dump_service.py:174 msgid "error importing tasks" @@ -569,7 +573,7 @@ msgstr "Enthält ungültige Benutzerfelder." #: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 #: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 msgid "Name duplicated for the project" -msgstr "" +msgstr "Der Name für das Projekt ist doppelt vergeben" #: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 msgid "Error generating project dump" @@ -781,6 +785,10 @@ msgid "" "

%(comment)s

\n" " " msgstr "" +"\n" +"

Kommentar

\n" +"

%(comment)s

\n" +" " #: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 #: taiga/users/admin.py:51 @@ -797,6 +805,12 @@ msgid "" "%(comment)s\n" "---------" msgstr "" +"---------\n" +"- Von: %(full_name)s <%(email)s>\n" +"---------\n" +"- Kommentar:\n" +"%(comment)s\n" +"---------" #: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:8 msgid "- Extra info:" @@ -808,6 +822,9 @@ msgid "" "\n" "[Taiga] Feedback from %(full_name)s <%(email)s>\n" msgstr "" +"\n" +"[Taiga] Feedback von %(full_name)s <%(email)s>\n" +" \n" #: taiga/hooks/api.py:52 msgid "The payload is not a valid json" @@ -905,6 +922,9 @@ msgid "" "\n" "{message}" msgstr "" +"Kommentar von GitHub:\n" +"\n" +"{message}" #: taiga/hooks/gitlab/event_hooks.py:87 msgid "Status changed from GitLab commit" @@ -926,7 +946,7 @@ msgstr "Meilensteine ansehen" #: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 msgid "View user stories" -msgstr "User Stories ansehen. " +msgstr "User-Stories ansehen. " #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 #: taiga/permissions/permissions.py:64 @@ -958,11 +978,11 @@ msgstr "Mitgliedschaft beantragen" #: taiga/permissions/permissions.py:40 msgid "Add user story to project" -msgstr "User Story zu Projekt hinzufügen" +msgstr "User-Story zu Projekt hinzufügen" #: taiga/permissions/permissions.py:41 msgid "Add comments to user stories" -msgstr "Kommentar zu User Stories hinzufügen" +msgstr "Kommentar zu User-Stories hinzufügen" #: taiga/permissions/permissions.py:42 msgid "Add comments to tasks" @@ -1006,19 +1026,19 @@ msgstr "Meilenstein löschen" #: taiga/permissions/permissions.py:59 msgid "View user story" -msgstr "User Story ansehen" +msgstr "User-Story ansehen" #: taiga/permissions/permissions.py:60 msgid "Add user story" -msgstr "User Story hinzufügen" +msgstr "User-Story hinzufügen" #: taiga/permissions/permissions.py:61 msgid "Modify user story" -msgstr "User Story ändern" +msgstr "User-Story ändern" #: taiga/permissions/permissions.py:62 msgid "Delete user story" -msgstr "User Story löschen" +msgstr "User-Story löschen" #: taiga/permissions/permissions.py:65 msgid "Add task" @@ -1482,7 +1502,7 @@ msgstr "voreingestellte Punkte" #: taiga/projects/models.py:97 msgid "default US status" -msgstr "voreingesteller User Story Status " +msgstr "voreingesteller User-Story Status " #: taiga/projects/models.py:101 msgid "default task status" @@ -1593,7 +1613,7 @@ msgstr "Vorgabe Optionen" #: taiga/projects/models.py:584 msgid "us statuses" -msgstr "User Story Status " +msgstr "User-Story Status " #: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 @@ -1682,6 +1702,11 @@ msgid "" "Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" "See issue #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"Ticket aktualisiert\n" +"Hallo %(user)s, %(changer)s hat ein Ticket aktualisiert in %(project)s\n" +"Ticket ansehen #%(ref)s %(subject)s auf %(url)s \n" +" \n" #: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 #, python-format @@ -1689,6 +1714,9 @@ msgid "" "\n" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Aktualisierte das Ticket #%(ref)s \"%(subject)s\"\n" +" \n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 #, python-format @@ -1715,6 +1743,13 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Neues Ticket wurde erstellt\n" +"Hallo %(user)s, %(changer)s hat ein neues Ticket erstellt in %(project)s\n" +"Ticket ansehen #%(ref)s %(subject)s auf %(url)s\n" +"\n" +"---\n" +"Das Taiga Team\n" #: taiga/projects/notifications/templates/emails/issues/issue-create-subject.jinja:1 #, python-format @@ -1722,6 +1757,9 @@ msgid "" "\n" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Erstellte das Ticket #%(ref)s \"%(subject)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 #, python-format @@ -1746,6 +1784,14 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Ticket gelöscht\n" +"Hallo %(user)s, %(changer)s hat ein Ticket gelöscht in %(project)s\n" +"Ticket #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-subject.jinja:1 #, python-format @@ -1753,6 +1799,10 @@ msgid "" "\n" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Löschte das Ticket #%(ref)s \"%(subject)s\"\n" +" \n" +"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 #, python-format @@ -1782,6 +1832,9 @@ msgid "" "\n" "[%(project)s] Updated the sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Aktualisierte den Sprint \"%(milestone)s\"\n" +" \n" #: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 #, python-format @@ -1815,6 +1868,9 @@ msgid "" "\n" "[%(project)s] Created the sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Erstellte den Sprint \"%(milestone)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 #, python-format @@ -1846,6 +1902,9 @@ msgid "" "\n" "[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Löschte den Sprint \"%(milestone)s\"\n" +" \n" #: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 #, python-format @@ -1859,6 +1918,15 @@ msgid "" "%(subject)s in Taiga\">See task\n" " " msgstr "" +"\n" +"

Aufgabe aktualisiert

\n" +"

Hallo %(user)s,
%(changer)s hat eine Aufgabe aktualisiert in " +"%(project)s

\n" +"

Aufgabe #%(ref)s %(subject)s

\n" +"Aufgabe ansehen \n" +"\n" +" " #: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 #, python-format @@ -1868,6 +1936,11 @@ msgid "" "Hello %(user)s, %(changer)s has updated a task on %(project)s\n" "See task #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"Aufgabe aktualisiert\n" +"Hallo %(user)s, %(changer)s hat eine Aufgabe aktualisiert in %(project)s\n" +"Aufgabe ansehen #%(ref)s %(subject)s auf %(url)s\n" +" \n" #: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 #, python-format @@ -1875,6 +1948,8 @@ msgid "" "\n" "[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Aktualisierte die Aufgabe #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 #, python-format @@ -1901,6 +1976,15 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Neue Aufgabe wurde erstellt\n" +"Hallo %(user)s, %(changer)s hat eine neue Aufgabe erstellt in %(project)s\n" +"Aufgabe ansehen #%(ref)s %(subject)s auf %(url)s\n" +"\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/tasks/task-create-subject.jinja:1 #, python-format @@ -1908,6 +1992,9 @@ msgid "" "\n" "[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Erstellte die Aufgabe #%(ref)s \"%(subject)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 #, python-format @@ -2142,7 +2229,7 @@ msgstr "Die Watcher beinhalten einen ungültigen Benutzer" #: taiga/projects/occ/mixins.py:58 msgid "The version parameter is not valid" -msgstr "" +msgstr "Der Versionsparameter ist ungültig" #: taiga/projects/occ/mixins.py:74 msgid "The version doesn't match with the current one" @@ -2308,6 +2395,11 @@ msgid "" msgstr "" "\n" "\n" +"---\n" +"\n" +"Das Taiga Team\n" +" \n" +"\n" #: taiga/projects/templates/emails/membership_invitation-subject.jinja:1 #, python-format @@ -2554,7 +2646,7 @@ msgstr "Kritisch" #. Translators: User role #: taiga/projects/translations.py:170 msgid "UX" -msgstr "" +msgstr "UX" #. Translators: User role #: taiga/projects/translations.py:172 @@ -2621,7 +2713,7 @@ msgstr "erzeugt von Ticket" #: taiga/projects/userstories/validators.py:28 msgid "There's no user story with that id" -msgstr "Es gibt keine User Story mit dieser id" +msgstr "Es gibt keine User-Story mit dieser id" #: taiga/projects/validators.py:28 msgid "There's no project with that id" @@ -2650,11 +2742,11 @@ msgstr "Stimme" #: taiga/projects/wiki/api.py:60 msgid "'content' parameter is mandatory" -msgstr "" +msgstr "'content' Parameter ist erforderlich" #: taiga/projects/wiki/api.py:63 msgid "'project_id' parameter is mandatory" -msgstr "" +msgstr "'project_id' Parameter ist erforderlich" #: taiga/projects/wiki/models.py:36 msgid "last modifier" @@ -2706,7 +2798,7 @@ msgstr "Ungültiges aktuelles Passwort" #: taiga/users/api.py:203 msgid "Incomplete arguments" -msgstr "" +msgstr "Unvollständige Argumente" #: taiga/users/api.py:208 msgid "Invalid image format" From a3b5fd8feb0fd829af8e7a36f58712474ce58982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 24 Jun 2015 19:47:13 +0200 Subject: [PATCH 020/190] Issue#2803: Reduce the amount of data on search results --- taiga/searches/api.py | 14 +++++----- taiga/searches/serializers.py | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 taiga/searches/serializers.py diff --git a/taiga/searches/api.py b/taiga/searches/api.py index 6aa2060d..e4934194 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -20,13 +20,11 @@ from taiga.base.api import viewsets from taiga.base import response from taiga.base.api.utils import get_object_or_404 -from taiga.projects.userstories.serializers import UserStorySerializer -from taiga.projects.tasks.serializers import TaskSerializer -from taiga.projects.issues.serializers import IssueSerializer -from taiga.projects.wiki.serializers import WikiPageSerializer from taiga.permissions.service import user_has_perm from . import services +from . import serializers + class SearchViewSet(viewsets.ViewSet): @@ -55,20 +53,20 @@ class SearchViewSet(viewsets.ViewSet): def _search_user_stories(self, project, text): queryset = services.search_user_stories(project, text) - serializer = UserStorySerializer(queryset, many=True) + serializer = serializers.UserStorySearchResultsSerializer(queryset, many=True) return serializer.data def _search_tasks(self, project, text): queryset = services.search_tasks(project, text) - serializer = TaskSerializer(queryset, many=True) + serializer = serializers.TaskSearchResultsSerializer(queryset, many=True) return serializer.data def _search_issues(self, project, text): queryset = services.search_issues(project, text) - serializer = IssueSerializer(queryset, many=True) + serializer = serializers.IssueSearchResultsSerializer(queryset, many=True) return serializer.data def _search_wiki_pages(self, project, text): queryset = services.search_wiki_pages(project, text) - serializer = WikiPageSerializer(queryset, many=True) + serializer = serializers.WikiPageSearchResultsSerializer(queryset, many=True) return serializer.data diff --git a/taiga/searches/serializers.py b/taiga/searches/serializers.py new file mode 100644 index 00000000..ee713fff --- /dev/null +++ b/taiga/searches/serializers.py @@ -0,0 +1,49 @@ +# 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.issues.serializers import IssueSerializer +from taiga.projects.userstories.serializers import UserStorySerializer +from taiga.projects.tasks.serializers import TaskSerializer +from taiga.projects.wiki.serializers import WikiPageSerializer + +from taiga.projects.issues.models import Issue +from taiga.projects.userstories.models import UserStory +from taiga.projects.tasks.models import Task +from taiga.projects.wiki.models import WikiPage + + +class IssueSearchResultsSerializer(IssueSerializer): + class Meta: + model = Issue + fields = ('id', 'ref', 'subject', 'status', 'assigned_to') + + +class TaskSearchResultsSerializer(TaskSerializer): + class Meta: + model = Task + fields = ('id', 'ref', 'subject', 'status', 'assigned_to') + + +class UserStorySearchResultsSerializer(UserStorySerializer): + class Meta: + model = UserStory + fields = ('id', 'ref', 'subject', 'status', 'total_points') + + +class WikiPageSearchResultsSerializer(WikiPageSerializer): + class Meta: + model = WikiPage + fields = ('id', 'slug') From aaf89ebb048a91091a8f52523c72bf02cd59f6e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 23 Jun 2015 17:24:17 +0200 Subject: [PATCH 021/190] Issue#2943: Regenerate refs for tasks, issues and user stories on project change --- taiga/projects/issues/api.py | 58 ++++++++- taiga/projects/references/models.py | 30 +++-- taiga/projects/tasks/api.py | 42 ++++++- taiga/projects/userstories/api.py | 38 ++++++ tests/factories.py | 1 + .../test_issues_resources.py | 113 +++++++++++++++++ .../test_tasks_resources.py | 114 +++++++++++++++++- .../test_userstories_resources.py | 107 +++++++++++++++- tests/integration/test_history.py | 4 +- tests/integration/test_notifications.py | 27 +++-- .../integration/test_references_sequences.py | 67 ++++++++++ tests/integration/test_stats.py | 12 +- tests/integration/test_tasks.py | 5 +- tests/integration/test_userstories.py | 4 +- 14 files changed, 575 insertions(+), 47 deletions(-) diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index d2582223..6f59a93a 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -31,7 +31,8 @@ from taiga.projects.notifications.mixins import WatchedResourceMixin from taiga.projects.occ import OCCResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin -from taiga.projects.models import Project +from taiga.projects.models import Project, IssueStatus, Severity, Priority, IssueType +from taiga.projects.milestones.models import Milestone from taiga.projects.votes.utils import attach_votescount_to_queryset from taiga.projects.votes import services as votes_service from taiga.projects.votes import serializers as votes_serializers @@ -121,6 +122,60 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, "assigned_to", "subject") + def update(self, request, *args, **kwargs): + self.object = self.get_object_or_none() + project_id = request.DATA.get('project', None) + if project_id and self.object and self.object.project.id != project_id: + try: + new_project = Project.objects.get(pk=project_id) + self.check_permissions(request, "destroy", self.object) + self.check_permissions(request, "create", new_project) + + sprint_id = request.DATA.get('milestone', None) + if sprint_id is not None and new_project.milestones.filter(pk=sprint_id).count() == 0: + request.DATA['milestone'] = None + + status_id = request.DATA.get('status', None) + if status_id is not None: + try: + old_status = self.object.project.issue_statuses.get(pk=status_id) + new_status = new_project.issue_statuses.get(slug=old_status.slug) + request.DATA['status'] = new_status.id + except IssueStatus.DoesNotExist: + request.DATA['status'] = new_project.default_issue_status.id + + priority_id = request.DATA.get('priority', None) + if priority_id is not None: + try: + old_priority = self.object.project.priorities.get(pk=priority_id) + new_priority = new_project.priorities.get(name=old_priority.name) + request.DATA['priority'] = new_priority.id + except Priority.DoesNotExist: + request.DATA['priority'] = new_project.default_priority.id + + severity_id = request.DATA.get('severity', None) + if severity_id is not None: + try: + old_severity = self.object.project.severities.get(pk=severity_id) + new_severity = new_project.severities.get(name=old_severity.name) + request.DATA['severity'] = new_severity.id + except Severity.DoesNotExist: + request.DATA['severity'] = new_project.default_severity.id + + type_id = request.DATA.get('type', None) + if type_id is not None: + try: + old_type = self.object.project.issue_types.get(pk=type_id) + new_type = new_project.issue_types.get(name=old_type.name) + request.DATA['type'] = new_type.id + except IssueType.DoesNotExist: + request.DATA['type'] = new_project.default_issue_type.id + + except Project.DoesNotExist: + return response.BadRequest(_("The project doesn't exist")) + + return super().update(request, *args, **kwargs) + def get_queryset(self): qs = models.Issue.objects.all() qs = qs.prefetch_related("attachments") @@ -130,6 +185,7 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, def pre_save(self, obj): if not obj.id: obj.owner = self.request.user + super().pre_save(obj) def pre_conditions_on_save(self, obj): diff --git a/taiga/projects/references/models.py b/taiga/projects/references/models.py index 8832034c..583c69c8 100644 --- a/taiga/projects/references/models.py +++ b/taiga/projects/references/models.py @@ -80,19 +80,31 @@ def delete_sequence(sender, instance, **kwargs): seq.delete(seqname) -def attach_sequence(sender, instance, created, **kwargs): - if created and not instance._importing: - # Create a reference object. This operation should be - # used in transaction context, otherwise it can - # create a lot of phantom reference objects. - refval, _ = make_reference(instance, instance.project) +def store_previous_project(sender, instance, **kwargs): + try: + prev_instance = sender.objects.get(pk=instance.pk) + instance.prev_project = prev_instance.project + except sender.DoesNotExist: + instance.prev_project = None - # Additionally, attach sequence number to instance as ref - instance.ref = refval - instance.save(update_fields=['ref']) + +def attach_sequence(sender, instance, created, **kwargs): + if not instance._importing: + if created or instance.prev_project != instance.project: + # Create a reference object. This operation should be + # used in transaction context, otherwise it can + # create a lot of phantom reference objects. + refval, _ = make_reference(instance, instance.project) + + # Additionally, attach sequence number to instance as ref + instance.ref = refval + instance.save(update_fields=['ref']) models.signals.post_save.connect(create_sequence, sender=Project, dispatch_uid="refproj") +models.signals.pre_save.connect(store_previous_project, sender=UserStory, dispatch_uid="refus") +models.signals.pre_save.connect(store_previous_project, sender=Issue, dispatch_uid="refissue") +models.signals.pre_save.connect(store_previous_project, sender=Task, dispatch_uid="reftask") models.signals.post_save.connect(attach_sequence, sender=UserStory, dispatch_uid="refus") models.signals.post_save.connect(attach_sequence, sender=Issue, dispatch_uid="refissue") models.signals.post_save.connect(attach_sequence, sender=Task, dispatch_uid="reftask") diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 2f1c008b..5d5581cd 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -21,7 +21,7 @@ from taiga.base import filters, response from taiga.base import exceptions as exc from taiga.base.decorators import list_route from taiga.base.api import ModelCrudViewSet -from taiga.projects.models import Project +from taiga.projects.models import Project, TaskStatus from django.http import HttpResponse from taiga.projects.notifications.mixins import WatchedResourceMixin @@ -44,6 +44,38 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, filter_fields = ["user_story", "milestone", "project", "assigned_to", "status__is_closed", "watchers"] + def update(self, request, *args, **kwargs): + self.object = self.get_object_or_none() + project_id = request.DATA.get('project', None) + if project_id and self.object and self.object.project.id != project_id: + try: + new_project = Project.objects.get(pk=project_id) + self.check_permissions(request, "destroy", self.object) + self.check_permissions(request, "create", new_project) + + sprint_id = request.DATA.get('milestone', None) + if sprint_id is not None and new_project.milestones.filter(pk=sprint_id).count() == 0: + request.DATA['milestone'] = None + + us_id = request.DATA.get('user_story', None) + if us_id is not None and new_project.user_stories.filter(pk=us_id).count() == 0: + request.DATA['user_story'] = None + + status_id = request.DATA.get('status', None) + if status_id is not None: + try: + old_status = self.object.project.task_statuses.get(pk=status_id) + new_status = new_project.task_statuses.get(slug=old_status.slug) + request.DATA['status'] = new_status.id + except TaskStatus.DoesNotExist: + request.DATA['status'] = new_project.default_task_status.id + + except Project.DoesNotExist: + return response.BadRequest(_("The project doesn't exist")) + + return super().update(request, *args, **kwargs) + + def pre_save(self, obj): if obj.user_story: obj.milestone = obj.user_story.milestone @@ -55,16 +87,16 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, super().pre_conditions_on_save(obj) if obj.milestone and obj.milestone.project != obj.project: - raise exc.WrongArguments(_("You don't have permissions for add/modify this task.")) + raise exc.WrongArguments(_("You don't have permissions to set this sprint to this task.")) if obj.user_story and obj.user_story.project != obj.project: - raise exc.WrongArguments(_("You don't have permissions for add/modify this task.")) + raise exc.WrongArguments(_("You don't have permissions to set this user story to this task.")) if obj.status and obj.status.project != obj.project: - raise exc.WrongArguments(_("You don't have permissions for add/modify this task.")) + raise exc.WrongArguments(_("You don't have permissions to set this status to this task.")) if obj.milestone and obj.user_story and obj.milestone != obj.user_story.milestone: - raise exc.WrongArguments(_("You don't have permissions for add/modify this task.")) + raise exc.WrongArguments(_("You don't have permissions to set this sprint to this task.")) @list_route(methods=["GET"]) def by_ref(self, request): diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index b3e6ed05..9820f589 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -62,6 +62,33 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi # Specific filter used for filtering neighbor user stories _neighbor_tags_filter = filters.TagsFilter('neighbor_tags') + def update(self, request, *args, **kwargs): + self.object = self.get_object_or_none() + project_id = request.DATA.get('project', None) + if project_id and self.object and self.object.project.id != project_id: + try: + new_project = Project.objects.get(pk=project_id) + self.check_permissions(request, "destroy", self.object) + self.check_permissions(request, "create", new_project) + + sprint_id = request.DATA.get('milestone', None) + if sprint_id is not None and new_project.milestones.filter(pk=sprint_id).count() == 0: + request.DATA['milestone'] = None + + status_id = request.DATA.get('status', None) + if status_id is not None: + try: + old_status = self.object.project.us_statuses.get(pk=status_id) + new_status = new_project.us_statuses.get(slug=old_status.slug) + request.DATA['status'] = new_status.id + except UserStoryStatus.DoesNotExist: + request.DATA['status'] = new_project.default_us_status.id + except Project.DoesNotExist: + return response.BadRequest(_("The project doesn't exist")) + + return super().update(request, *args, **kwargs) + + def get_queryset(self): qs = self.model.objects.all() qs = qs.prefetch_related("role_points", @@ -100,6 +127,17 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi super().post_save(obj, created) + def pre_conditions_on_save(self, obj): + super().pre_conditions_on_save(obj) + + if obj.milestone and obj.milestone.project != obj.project: + raise exc.PermissionDenied(_("You don't have permissions to set this sprint " + "to this user story.")) + + if obj.status and obj.status.project != obj.project: + raise exc.PermissionDenied(_("You don't have permissions to set this status " + "to this user story.")) + @list_route(methods=["GET"]) def by_ref(self, request): ref = request.QUERY_PARAMS.get("ref", None) diff --git a/tests/factories.py b/tests/factories.py index 4e9b9d0c..8a351d49 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -231,6 +231,7 @@ class UserStoryFactory(Factory): subject = factory.Sequence(lambda n: "User Story {}".format(n)) description = factory.Sequence(lambda n: "User Story {} description".format(n)) status = factory.SubFactory("tests.factories.UserStoryStatusFactory") + milestone = factory.SubFactory("tests.factories.MilestoneFactory") class UserStoryStatusFactory(Factory): diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index c6c99f2d..43abdeac 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -160,6 +160,119 @@ def test_issue_update(client, data): assert results == [401, 403, 403, 200, 200] +def test_issue_update_with_project_change(client): + user1 = f.UserFactory.create() + user2 = f.UserFactory.create() + user3 = f.UserFactory.create() + user4 = f.UserFactory.create() + project1 = f.ProjectFactory() + project2 = f.ProjectFactory() + + issue_status1 = f.IssueStatusFactory.create(project=project1) + issue_status2 = f.IssueStatusFactory.create(project=project2) + + priority1 = f.PriorityFactory.create(project=project1) + priority2 = f.PriorityFactory.create(project=project2) + + severity1 = f.SeverityFactory.create(project=project1) + severity2 = f.SeverityFactory.create(project=project2) + + issue_type1 = f.IssueTypeFactory.create(project=project1) + issue_type2 = f.IssueTypeFactory.create(project=project2) + + project1.default_issue_status = issue_status1 + project2.default_issue_status = issue_status2 + + project1.default_priority = priority1 + project2.default_priority = priority2 + + project1.default_severity = severity1 + project2.default_severity = severity2 + + project1.default_issue_type = issue_type1 + project2.default_issue_type = issue_type2 + + project1.save() + project2.save() + + membership1 = f.MembershipFactory(project=project1, + user=user1, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership2 = f.MembershipFactory(project=project2, + user=user1, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership3 = f.MembershipFactory(project=project1, + user=user2, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership4 = f.MembershipFactory(project=project2, + user=user3, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + issue = f.IssueFactory.create(project=project1) + + url = reverse('issues-detail', kwargs={"pk": issue.pk}) + + # Test user with permissions in both projects + client.login(user1) + + issue_data = IssueSerializer(issue).data + issue_data["project"] = project2.id + issue_data = json.dumps(issue_data) + + response = client.put(url, data=issue_data, content_type="application/json") + + assert response.status_code == 200 + + issue.project = project1 + issue.save() + + # Test user with permissions in only origin project + client.login(user2) + + issue_data = IssueSerializer(issue).data + issue_data["project"] = project2.id + issue_data = json.dumps(issue_data) + + response = client.put(url, data=issue_data, content_type="application/json") + + assert response.status_code == 403 + + issue.project = project1 + issue.save() + + # Test user with permissions in only destionation project + client.login(user3) + + issue_data = IssueSerializer(issue).data + issue_data["project"] = project2.id + issue_data = json.dumps(issue_data) + + response = client.put(url, data=issue_data, content_type="application/json") + + assert response.status_code == 403 + + issue.project = project1 + issue.save() + + # Test user without permissions in the projects + client.login(user4) + + issue_data = IssueSerializer(issue).data + issue_data["project"] = project2.id + issue_data = json.dumps(issue_data) + + response = client.put(url, data=issue_data, content_type="application/json") + + assert response.status_code == 403 + + issue.project = project1 + issue.save() + + def test_issue_delete(client, data): public_url = reverse('issues-detail', kwargs={"pk": data.public_issue.pk}) private_url1 = reverse('issues-detail', kwargs={"pk": data.private_issue1.pk}) diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index 22bb719f..2259cfa4 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -83,18 +83,25 @@ def data(): user=m.project_owner, is_owner=True) + milestone_public_task = f.MilestoneFactory(project=m.public_project) + milestone_private_task1 = f.MilestoneFactory(project=m.private_project1) + milestone_private_task2 = f.MilestoneFactory(project=m.private_project2) + m.public_task = f.TaskFactory(project=m.public_project, status__project=m.public_project, - milestone__project=m.public_project, - user_story__project=m.public_project) + milestone=milestone_public_task, + user_story__project=m.public_project, + user_story__milestone=milestone_public_task) m.private_task1 = f.TaskFactory(project=m.private_project1, status__project=m.private_project1, - milestone__project=m.private_project1, - user_story__project=m.private_project1) + milestone=milestone_private_task1, + user_story__project=m.private_project1, + user_story__milestone=milestone_private_task1) m.private_task2 = f.TaskFactory(project=m.private_project2, status__project=m.private_project2, - milestone__project=m.private_project2, - user_story__project=m.private_project2) + milestone=milestone_private_task2, + user_story__project=m.private_project2, + user_story__milestone=milestone_private_task2) m.public_project.default_task_status = m.public_task.status m.public_project.save() @@ -160,6 +167,101 @@ def test_task_update(client, data): assert results == [401, 403, 403, 200, 200] +def test_task_update_with_project_change(client): + user1 = f.UserFactory.create() + user2 = f.UserFactory.create() + user3 = f.UserFactory.create() + user4 = f.UserFactory.create() + project1 = f.ProjectFactory() + project2 = f.ProjectFactory() + + task_status1 = f.TaskStatusFactory.create(project=project1) + task_status2 = f.TaskStatusFactory.create(project=project2) + + project1.default_task_status = task_status1 + project2.default_task_status = task_status2 + + project1.save() + project2.save() + + membership1 = f.MembershipFactory(project=project1, + user=user1, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership2 = f.MembershipFactory(project=project2, + user=user1, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership3 = f.MembershipFactory(project=project1, + user=user2, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership4 = f.MembershipFactory(project=project2, + user=user3, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + task = f.TaskFactory.create(project=project1) + + url = reverse('tasks-detail', kwargs={"pk": task.pk}) + + # Test user with permissions in both projects + client.login(user1) + + task_data = TaskSerializer(task).data + task_data["project"] = project2.id + task_data = json.dumps(task_data) + + response = client.put(url, data=task_data, content_type="application/json") + + assert response.status_code == 200 + + task.project = project1 + task.save() + + # Test user with permissions in only origin project + client.login(user2) + + task_data = TaskSerializer(task).data + task_data["project"] = project2.id + task_data = json.dumps(task_data) + + response = client.put(url, data=task_data, content_type="application/json") + + assert response.status_code == 403 + + task.project = project1 + task.save() + + # Test user with permissions in only destionation project + client.login(user3) + + task_data = TaskSerializer(task).data + task_data["project"] = project2.id + task_data = json.dumps(task_data) + + response = client.put(url, data=task_data, content_type="application/json") + + assert response.status_code == 403 + + task.project = project1 + task.save() + + # Test user without permissions in the projects + client.login(user4) + + task_data = TaskSerializer(task).data + task_data["project"] = project2.id + task_data = json.dumps(task_data) + + response = client.put(url, data=task_data, content_type="application/json") + + assert response.status_code == 403 + + task.project = project1 + task.save() + + def test_task_delete(client, data): public_url = reverse('tasks-detail', kwargs={"pk": data.public_task.pk}) private_url1 = reverse('tasks-detail', kwargs={"pk": data.private_task1.pk}) diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index 3a718cc7..a3e09808 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -89,13 +89,19 @@ def data(): m.public_role_points = f.RolePointsFactory(role=m.public_project.roles.all()[0], points=m.public_points, - user_story__project=m.public_project) + user_story__project=m.public_project, + user_story__milestone__project=m.public_project, + user_story__status__project=m.public_project) m.private_role_points1 = f.RolePointsFactory(role=m.private_project1.roles.all()[0], points=m.private_points1, - user_story__project=m.private_project1) + user_story__project=m.private_project1, + user_story__milestone__project=m.private_project1, + user_story__status__project=m.private_project1) m.private_role_points2 = f.RolePointsFactory(role=m.private_project2.roles.all()[0], points=m.private_points2, - user_story__project=m.private_project2) + user_story__project=m.private_project2, + user_story__milestone__project=m.private_project2, + user_story__status__project=m.private_project2) m.public_user_story = m.public_role_points.user_story m.private_user_story1 = m.private_role_points1.user_story @@ -158,6 +164,101 @@ def test_user_story_update(client, data): assert results == [401, 403, 403, 200, 200] +def test_user_story_update_with_project_change(client): + user1 = f.UserFactory.create() + user2 = f.UserFactory.create() + user3 = f.UserFactory.create() + user4 = f.UserFactory.create() + project1 = f.ProjectFactory() + project2 = f.ProjectFactory() + + us_status1 = f.UserStoryStatusFactory.create(project=project1) + us_status2 = f.UserStoryStatusFactory.create(project=project2) + + project1.default_us_status = us_status1 + project2.default_us_status = us_status2 + + project1.save() + project2.save() + + membership1 = f.MembershipFactory(project=project1, + user=user1, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership2 = f.MembershipFactory(project=project2, + user=user1, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership3 = f.MembershipFactory(project=project1, + user=user2, + role__project=project1, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + membership4 = f.MembershipFactory(project=project2, + user=user3, + role__project=project2, + role__permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) + + us = f.UserStoryFactory.create(project=project1) + + url = reverse('userstories-detail', kwargs={"pk": us.pk}) + + # Test user with permissions in both projects + client.login(user1) + + us_data = UserStorySerializer(us).data + us_data["project"] = project2.id + us_data = json.dumps(us_data) + + response = client.put(url, data=us_data, content_type="application/json") + + assert response.status_code == 200 + + us.project = project1 + us.save() + + # Test user with permissions in only origin project + client.login(user2) + + us_data = UserStorySerializer(us).data + us_data["project"] = project2.id + us_data = json.dumps(us_data) + + response = client.put(url, data=us_data, content_type="application/json") + + assert response.status_code == 403 + + us.project = project1 + us.save() + + # Test user with permissions in only destionation project + client.login(user3) + + us_data = UserStorySerializer(us).data + us_data["project"] = project2.id + us_data = json.dumps(us_data) + + response = client.put(url, data=us_data, content_type="application/json") + + assert response.status_code == 403 + + us.project = project1 + us.save() + + # Test user without permissions in the projects + client.login(user4) + + us_data = UserStorySerializer(us).data + us_data["project"] = project2.id + us_data = json.dumps(us_data) + + response = client.put(url, data=us_data, content_type="application/json") + + assert response.status_code == 403 + + us.project = project1 + us.save() + + def test_user_story_delete(client, data): public_url = reverse('userstories-detail', kwargs={"pk": data.public_user_story.pk}) private_url1 = reverse('userstories-detail', kwargs={"pk": data.private_user_story1.pk}) diff --git a/tests/integration/test_history.py b/tests/integration/test_history.py index 020eadcd..4ea8efbd 100644 --- a/tests/integration/test_history.py +++ b/tests/integration/test_history.py @@ -199,7 +199,7 @@ def test_take_hidden_snapshot(): def test_history_with_only_comment_shouldnot_be_hidden(client): project = f.create_project() - us = f.create_userstory(project=project) + us = f.create_userstory(project=project, status__project=project) f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) qs_all = HistoryEntry.objects.all() @@ -213,7 +213,7 @@ def test_history_with_only_comment_shouldnot_be_hidden(client): client.login(project.owner) response = client.patch(url, data, content_type="application/json") - assert response.status_code == 200, response.content + assert response.status_code == 200, str(response.content) assert qs_all.count() == 1 assert qs_hidden.count() == 0 diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index d3d64ddb..13087254 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -32,6 +32,7 @@ from taiga.projects.history.services import take_snapshot from taiga.projects.issues.serializers import IssueSerializer from taiga.projects.userstories.serializers import UserStorySerializer from taiga.projects.tasks.serializers import TaskSerializer +from taiga.permissions.permissions import MEMBERS_PERMISSIONS pytestmark = pytest.mark.django_db @@ -317,7 +318,7 @@ def test_watchers_assignation_for_issue(client): url = reverse("issues-detail", args=[issue.pk]) response = client.json.patch(url, json.dumps(data)) - assert response.status_code == 200, response.content + assert response.status_code == 200, str(response.content) issue = f.create_issue(project=project1, owner=user1) data = {"version": issue.version, @@ -356,22 +357,22 @@ def test_watchers_assignation_for_task(client): user2 = f.UserFactory.create() project1 = f.ProjectFactory.create(owner=user1) project2 = f.ProjectFactory.create(owner=user2) - role1 = f.RoleFactory.create(project=project1) + role1 = f.RoleFactory.create(project=project1, permissions=list(map(lambda x: x[0], MEMBERS_PERMISSIONS))) role2 = f.RoleFactory.create(project=project2) f.MembershipFactory.create(project=project1, user=user1, role=role1, is_owner=True) f.MembershipFactory.create(project=project2, user=user2, role=role2) client.login(user1) - task = f.create_task(project=project1, owner=user1) + task = f.create_task(project=project1, owner=user1, status__project=project1, milestone__project=project1, user_story=None) data = {"version": task.version, "watchers": [user1.pk]} url = reverse("tasks-detail", args=[task.pk]) response = client.json.patch(url, json.dumps(data)) - assert response.status_code == 200, response.content + assert response.status_code == 200, str(response.content) - task = f.create_task(project=project1, owner=user1) + task = f.create_task(project=project1, owner=user1, status__project=project1, milestone__project=project1) data = {"version": task.version, "watchers": [user1.pk, user2.pk]} @@ -379,7 +380,7 @@ def test_watchers_assignation_for_task(client): response = client.json.patch(url, json.dumps(data)) assert response.status_code == 400 - task = f.create_task(project=project1, owner=user1) + task = f.create_task(project=project1, owner=user1, status__project=project1, milestone__project=project1) data = dict(TaskSerializer(task).data) data["id"] = None data["version"] = None @@ -391,7 +392,7 @@ def test_watchers_assignation_for_task(client): # Test the impossible case when project is not # exists in create request, and validator works as expected - task = f.create_task(project=project1, owner=user1) + task = f.create_task(project=project1, owner=user1, status__project=project1, milestone__project=project1) data = dict(TaskSerializer(task).data) data["id"] = None @@ -415,15 +416,15 @@ def test_watchers_assignation_for_us(client): client.login(user1) - us = f.create_userstory(project=project1, owner=user1) + us = f.create_userstory(project=project1, owner=user1, status__project=project1) data = {"version": us.version, "watchers": [user1.pk]} url = reverse("userstories-detail", args=[us.pk]) response = client.json.patch(url, json.dumps(data)) - assert response.status_code == 200 + assert response.status_code == 200, str(response.content) - us = f.create_userstory(project=project1, owner=user1) + us = f.create_userstory(project=project1, owner=user1, status__project=project1) data = {"version": us.version, "watchers": [user1.pk, user2.pk]} @@ -431,7 +432,7 @@ def test_watchers_assignation_for_us(client): response = client.json.patch(url, json.dumps(data)) assert response.status_code == 400 - us = f.create_userstory(project=project1, owner=user1) + us = f.create_userstory(project=project1, owner=user1, status__project=project1) data = dict(UserStorySerializer(us).data) data["id"] = None data["version"] = None @@ -443,7 +444,7 @@ def test_watchers_assignation_for_us(client): # Test the impossible case when project is not # exists in create request, and validator works as expected - us = f.create_userstory(project=project1, owner=user1) + us = f.create_userstory(project=project1, owner=user1, status__project=project1) data = dict(UserStorySerializer(us).data) data["id"] = None @@ -463,4 +464,4 @@ def test_retrieve_notify_policies_by_anonymous_user(client): url = reverse("notifications-detail", args=[policy.pk]) response = client.get(url, content_type="application/json") assert response.status_code == 404, response.status_code - assert response.data["_error_message"] == "No NotifyPolicy matches the given query.", response.content + assert response.data["_error_message"] == "No NotifyPolicy matches the given query.", str(response.content) diff --git a/tests/integration/test_references_sequences.py b/tests/integration/test_references_sequences.py index 10653cea..f384791e 100644 --- a/tests/integration/test_references_sequences.py +++ b/tests/integration/test_references_sequences.py @@ -74,3 +74,70 @@ def test_unique_reference_per_project(seq, refmodels): project.delete() assert not seq.exists(seqname) + + +@pytest.mark.django_db +def test_regenerate_us_reference_on_project_change(seq, refmodels): + project1 = factories.ProjectFactory.create() + seqname1 = refmodels.make_sequence_name(project1) + project2 = factories.ProjectFactory.create() + seqname2 = refmodels.make_sequence_name(project2) + + seq.alter(seqname1, 100) + seq.alter(seqname2, 200) + + user_story = factories.UserStoryFactory.create(project=project1) + assert user_story.ref == 101 + + user_story.subject = "other" + user_story.save() + assert user_story.ref == 101 + + user_story.project = project2 + user_story.save() + + assert user_story.ref == 201 + +@pytest.mark.django_db +def test_regenerate_task_reference_on_project_change(seq, refmodels): + project1 = factories.ProjectFactory.create() + seqname1 = refmodels.make_sequence_name(project1) + project2 = factories.ProjectFactory.create() + seqname2 = refmodels.make_sequence_name(project2) + + seq.alter(seqname1, 100) + seq.alter(seqname2, 200) + + task = factories.TaskFactory.create(project=project1) + assert task.ref == 101 + + task.subject = "other" + task.save() + assert task.ref == 101 + + task.project = project2 + task.save() + + assert task.ref == 201 + +@pytest.mark.django_db +def test_regenerate_issue_reference_on_project_change(seq, refmodels): + project1 = factories.ProjectFactory.create() + seqname1 = refmodels.make_sequence_name(project1) + project2 = factories.ProjectFactory.create() + seqname2 = refmodels.make_sequence_name(project2) + + seq.alter(seqname1, 100) + seq.alter(seqname2, 200) + + issue = factories.IssueFactory.create(project=project1) + assert issue.ref == 101 + + issue.subject = "other" + issue.save() + assert issue.ref == 101 + + issue.project = project2 + issue.save() + + assert issue.ref == 201 diff --git a/tests/integration/test_stats.py b/tests/integration/test_stats.py index 97bce3ff..4dfab9e4 100644 --- a/tests/integration/test_stats.py +++ b/tests/integration/test_stats.py @@ -37,19 +37,23 @@ def data(): m.role_points1 = f.RolePointsFactory(role=m.role1, points=m.points1, user_story__project=m.project, - user_story__status=m.open_status) + user_story__status=m.open_status, + user_story__milestone=None) m.role_points2 = f.RolePointsFactory(role=m.role1, points=m.points2, user_story__project=m.project, - user_story__status=m.open_status) + user_story__status=m.open_status, + user_story__milestone=None) m.role_points3 = f.RolePointsFactory(role=m.role1, points=m.points3, user_story__project=m.project, - user_story__status=m.open_status) + user_story__status=m.open_status, + user_story__milestone=None) m.role_points4 = f.RolePointsFactory(role=m.project.roles.all()[0], points=m.points4, user_story__project=m.project, - user_story__status=m.open_status) + user_story__status=m.open_status, + user_story__milestone=None) m.user_story1 = m.role_points1.user_story m.user_story2 = m.role_points2.user_story diff --git a/tests/integration/test_tasks.py b/tests/integration/test_tasks.py index 61d1954a..382bfc6f 100644 --- a/tests/integration/test_tasks.py +++ b/tests/integration/test_tasks.py @@ -54,8 +54,9 @@ def test_create_task_without_status(client): def test_api_update_task_tags(client): - task = f.create_task() - f.MembershipFactory.create(project=task.project, user=task.owner, is_owner=True) + project = f.ProjectFactory.create() + task = f.create_task(project=project, status__project=project, milestone=None, user_story=None) + f.MembershipFactory.create(project=project, user=task.owner, is_owner=True) url = reverse("tasks-detail", kwargs={"pk": task.pk}) data = {"tags": ["back", "front"], "version": task.version} diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index e86f1dad..90b85444 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -152,7 +152,7 @@ def test_update_userstory_points(client): f.PointsFactory.create(project=project, value=1) points3 = f.PointsFactory.create(project=project, value=2) - us = f.UserStoryFactory.create(project=project, owner=user1) + us = f.UserStoryFactory.create(project=project, owner=user1, status__project=project, milestone__project=project) usdata = UserStorySerializer(us).data url = reverse("userstories-detail", args=[us.pk]) @@ -166,7 +166,7 @@ def test_update_userstory_points(client): data["points"].update({'2000': points3.pk}) response = client.json.patch(url, json.dumps(data)) - assert response.status_code == 200 + assert response.status_code == 200, str(response.content) assert response.data["points"] == usdata['points'] # Api should save successful From 4480cb474ef240fd6f8ee153f97d4e46398c0da7 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 24 Jun 2015 14:10:50 +0200 Subject: [PATCH 022/190] Issue 2818 - When I comment a story, I should be "Involved" by the story --- taiga/projects/notifications/services.py | 3 +++ tests/integration/test_notifications.py | 30 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 67fb894a..75f7e48b 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -123,6 +123,9 @@ def analize_object_for_watchers(obj:object, history:object): for user in data["mentions"]: obj.watchers.add(user) + # Adding the person who edited the object to the watchers + if history.comment and not history.owner.is_system: + obj.watchers.add(history.owner) def _filter_by_permissions(obj, user): UserStory = apps.get_model("userstories", "UserStory") diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 13087254..a5a9847c 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -100,6 +100,36 @@ def test_analize_object_for_watchers(): assert issue.watchers.add.call_count == 2 +def test_analize_object_for_watchers_adding_owner_non_empty_comment(): + user1 = f.UserFactory.create() + + issue = MagicMock() + issue.description = "Foo" + issue.content = "" + + history = MagicMock() + history.comment = "Comment" + history.owner = user1 + + services.analize_object_for_watchers(issue, history) + assert issue.watchers.add.call_count == 1 + + +def test_analize_object_for_watchers_no_adding_owner_empty_comment(): + user1 = f.UserFactory.create() + + issue = MagicMock() + issue.description = "Foo" + issue.content = "" + + history = MagicMock() + history.comment = "" + history.owner = user1 + + services.analize_object_for_watchers(issue, history) + assert issue.watchers.add.call_count == 0 + + def test_users_to_notify(): project = f.ProjectFactory.create() role1 = f.RoleFactory.create(project=project, permissions=['view_issues']) From 3f369cf799d25b487c932ab6c63309e3f7a2abe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 25 Jun 2015 11:47:53 +0200 Subject: [PATCH 023/190] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b5fdcda..99d9e63e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ### Features - Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) - Allow multiple actions in the commit messages. +- Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer From b4a30f64afbedb2ff9cab3b60d800c5509457627 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 25 Jun 2015 13:50:36 +0200 Subject: [PATCH 024/190] Add full-text index to the search app --- taiga/searches/migrations/0001_initial.py | 41 +++++++++++++++++++++++ taiga/searches/migrations/__init__.py | 0 taiga/searches/services.py | 10 +++--- 3 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 taiga/searches/migrations/0001_initial.py create mode 100644 taiga/searches/migrations/__init__.py diff --git a/taiga/searches/migrations/0001_initial.py b/taiga/searches/migrations/0001_initial.py new file mode 100644 index 00000000..b30cfa4d --- /dev/null +++ b/taiga/searches/migrations/0001_initial.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('wiki', '0001_initial'), + ('userstories', '0009_remove_userstory_is_archived'), + ('issues', '0005_auto_20150623_1923'), + ('tasks', '0006_auto_20150623_1923'), + ] + + operations = [ + migrations.RunSQL( + """ + CREATE INDEX "userstories_full_text_idx" ON userstories_userstory USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); + """, + reverse_sql="""DROP INDEX IF EXISTS "userstories_full_text_idx";""" + ), + migrations.RunSQL( + """ + CREATE INDEX "tasks_full_text_idx" ON tasks_task USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); + """, + reverse_sql="""DROP INDEX IF EXISTS "tasks_full_text_idx";""" + ), + migrations.RunSQL( + """ + CREATE INDEX "issues_full_text_idx" ON issues_issue USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); + """, + reverse_sql="""DROP INDEX IF EXISTS "issues_full_text_idx";""" + ), + migrations.RunSQL( + """ + CREATE INDEX "wikipages_full_text_idx" ON wiki_wikipage USING gin(to_tsvector('simple', coalesce(slug, '') || ' ' || coalesce(content, ''))); + """, + reverse_sql="""DROP INDEX IF EXISTS "wikipages_full_text_idx";""" + ), + ] diff --git a/taiga/searches/migrations/__init__.py b/taiga/searches/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/searches/services.py b/taiga/searches/services.py index 1cc2374e..ccea5c19 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -23,9 +23,9 @@ MAX_RESULTS = getattr(settings, "SEARCHES_MAX_RESULTS", 150) def search_user_stories(project, text): model_cls = apps.get_model("userstories", "UserStory") - where_clause = ("to_tsvector(coalesce(userstories_userstory.subject) || ' ' || " + where_clause = ("to_tsvector('simple', coalesce(userstories_userstory.subject, '') || ' ' || " "coalesce(userstories_userstory.ref) || ' ' || " - "coalesce(userstories_userstory.description)) @@ to_tsquery(%s)") + "coalesce(userstories_userstory.description, '')) @@ to_tsquery(%s)") if text: text += ":*" @@ -37,7 +37,7 @@ def search_user_stories(project, text): def search_tasks(project, text): model_cls = apps.get_model("tasks", "Task") - where_clause = ("to_tsvector(coalesce(tasks_task.subject, '') || ' ' || " + where_clause = ("to_tsvector('simple', coalesce(tasks_task.subject, '') || ' ' || " "coalesce(tasks_task.ref) || ' ' || " "coalesce(tasks_task.description, '')) @@ to_tsquery(%s)") @@ -51,7 +51,7 @@ def search_tasks(project, text): def search_issues(project, text): model_cls = apps.get_model("issues", "Issue") - where_clause = ("to_tsvector(coalesce(issues_issue.subject) || ' ' || " + where_clause = ("to_tsvector('simple', coalesce(issues_issue.subject) || ' ' || " "coalesce(issues_issue.ref) || ' ' || " "coalesce(issues_issue.description)) @@ to_tsquery(%s)") @@ -65,7 +65,7 @@ def search_issues(project, text): def search_wiki_pages(project, text): model_cls = apps.get_model("wiki", "WikiPage") - where_clause = ("to_tsvector(coalesce(wiki_wikipage.slug) || ' ' || coalesce(wiki_wikipage.content)) " + where_clause = ("to_tsvector('simple', coalesce(wiki_wikipage.slug) || ' ' || coalesce(wiki_wikipage.content)) " "@@ to_tsquery(%s)") if text: From 30e08c61efe8152c3068de829e9d0ec88b411baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 25 Jun 2015 16:13:20 +0200 Subject: [PATCH 025/190] Excluded some unnecessary fields in ProjectMemberSerializer --- taiga/projects/serializers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index d34d5939..bc1de975 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -295,6 +295,7 @@ class ProjectMemberSerializer(serializers.ModelSerializer): class Meta: model = models.Membership + exclude = ("project", "email", "created_at", "token", "invited_by", "invitation_extra_text", "user_order") def get_photo(self, membership): return get_photo_or_gravatar_url(membership.user) From 8b05f206b210e773853933ccf053e16eefbc9391 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 25 Jun 2015 09:37:51 +0200 Subject: [PATCH 026/190] Concurrent queries for global search results --- taiga/searches/api.py | 35 +++++++++++++++++++++++------- tests/integration/test_searches.py | 2 +- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/taiga/searches/api.py b/taiga/searches/api.py index e4934194..befe539b 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -26,6 +26,7 @@ from . import services from . import serializers +from concurrent import futures class SearchViewSet(viewsets.ViewSet): def list(self, request, **kwargs): @@ -35,14 +36,32 @@ class SearchViewSet(viewsets.ViewSet): project = self._get_project(project_id) result = {} - if user_has_perm(request.user, "view_us", project): - result["userstories"] = self._search_user_stories(project, text) - if user_has_perm(request.user, "view_tasks", project): - result["tasks"] = self._search_tasks(project, text) - if user_has_perm(request.user, "view_issues", project): - result["issues"] = self._search_issues(project, text) - if user_has_perm(request.user, "view_wiki_pages", project): - result["wikipages"] = self._search_wiki_pages(project, text) + with futures.ThreadPoolExecutor(max_workers=4) as executor: + futures_list = [] + if user_has_perm(request.user, "view_us", project): + uss_future = executor.submit(self._search_user_stories, project, text) + uss_future.result_key = "userstories" + futures_list.append(uss_future) + if user_has_perm(request.user, "view_tasks", project): + tasks_future = executor.submit(self._search_tasks, project, text) + tasks_future.result_key = "tasks" + futures_list.append(tasks_future) + if user_has_perm(request.user, "view_issues", project): + issues_future = executor.submit(self._search_issues, project, text) + issues_future.result_key = "issues" + futures_list.append(issues_future) + if user_has_perm(request.user, "view_wiki_pages", project): + wiki_pages_future = executor.submit(self._search_wiki_pages, project, text) + wiki_pages_future.result_key = "wikipages" + futures_list.append(wiki_pages_future) + + for future in futures.as_completed(futures_list): + try: + data = future.result() + except Exception as exc: + print('%s generated an exception: %s' % (future.result_key, exc)) + else: + result[future.result_key] = data result["count"] = sum(map(lambda x: len(x), result.values())) return response.Ok(result) diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index 0837c4c5..f6a26cc1 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -25,7 +25,7 @@ from taiga.permissions.permissions import MEMBERS_PERMISSIONS from tests.utils import disconnect_signals, reconnect_signals -pytestmark = pytest.mark.django_db +pytestmark = pytest.mark.django_db(transaction=True) def setup_module(module): From 6d33c7821d80cbea2cd004b510f59090d93bc8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 25 Jun 2015 18:05:11 +0200 Subject: [PATCH 027/190] Make minor improvements over front sitemap --- taiga/front/sitemaps/generics.py | 8 ++++---- taiga/front/sitemaps/projects.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/taiga/front/sitemaps/generics.py b/taiga/front/sitemaps/generics.py index 180c6eb0..27fbc075 100644 --- a/taiga/front/sitemaps/generics.py +++ b/taiga/front/sitemaps/generics.py @@ -25,10 +25,10 @@ from .base import Sitemap class GenericSitemap(Sitemap): def items(self): return [ - {"url_key": "home", "changefreq": "monthly", "priority": 0.6}, - {"url_key": "login", "changefreq": "monthly", "priority": 0.6}, - {"url_key": "register", "changefreq": "monthly", "priority": 0.6}, - {"url_key": "forgot-password", "changefreq": "monthly", "priority": 0.6} + {"url_key": "home", "changefreq": "monthly", "priority": 1}, + {"url_key": "login", "changefreq": "monthly", "priority": 1}, + {"url_key": "register", "changefreq": "monthly", "priority": 1}, + {"url_key": "forgot-password", "changefreq": "monthly", "priority": 1} ] def location(self, obj): diff --git a/taiga/front/sitemaps/projects.py b/taiga/front/sitemaps/projects.py index bbbbfbb8..f9ad82f8 100644 --- a/taiga/front/sitemaps/projects.py +++ b/taiga/front/sitemaps/projects.py @@ -40,10 +40,10 @@ class ProjectsSitemap(Sitemap): return obj.modified_date def changefreq(self, obj): - return "daily" + return "hourly" def priority(self, obj): - return 0.6 + return 0.9 class ProjectBacklogsSitemap(Sitemap): From fad37091e8c1737bd45f4849fe2ea17bd02f50f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 25 Jun 2015 17:06:29 +0200 Subject: [PATCH 028/190] Issue#2650: Allow local network ips in gitlab and bitbucket ip filters --- taiga/hooks/bitbucket/api.py | 4 ++-- taiga/hooks/gitlab/api.py | 4 ++-- tests/integration/test_hooks_bitbucket.py | 19 +++++++++++++++++++ tests/integration/test_hooks_gitlab.py | 20 ++++++++++++++++++++ 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/taiga/hooks/bitbucket/api.py b/taiga/hooks/bitbucket/api.py index 562b5763..82b71ea0 100644 --- a/taiga/hooks/bitbucket/api.py +++ b/taiga/hooks/bitbucket/api.py @@ -24,7 +24,7 @@ from taiga.hooks.api import BaseWebhookApiViewSet from . import event_hooks from urllib.parse import parse_qs -from ipware.ip import get_real_ip +from ipware.ip import get_ip class BitBucketViewSet(BaseWebhookApiViewSet): @@ -60,7 +60,7 @@ class BitBucketViewSet(BaseWebhookApiViewSet): bitbucket_config = project.modules_config.config.get("bitbucket", {}) valid_origin_ips = bitbucket_config.get("valid_origin_ips", settings.BITBUCKET_VALID_ORIGIN_IPS) - origin_ip = get_real_ip(request) + origin_ip = get_ip(request) if valid_origin_ips and (not origin_ip or origin_ip not in valid_origin_ips): return False diff --git a/taiga/hooks/gitlab/api.py b/taiga/hooks/gitlab/api.py index 01e455c5..48d70fe7 100644 --- a/taiga/hooks/gitlab/api.py +++ b/taiga/hooks/gitlab/api.py @@ -16,7 +16,7 @@ from django.conf import settings -from ipware.ip import get_real_ip +from ipware.ip import get_ip from taiga.base.utils import json @@ -50,7 +50,7 @@ class GitLabViewSet(BaseWebhookApiViewSet): gitlab_config = project.modules_config.config.get("gitlab", {}) valid_origin_ips = gitlab_config.get("valid_origin_ips", settings.GITLAB_VALID_ORIGIN_IPS) - origin_ip = get_real_ip(request) + origin_ip = get_ip(request) if valid_origin_ips and (not origin_ip or origin_ip not in valid_origin_ips): return False diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index 9f19014d..ecb4058d 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -73,6 +73,25 @@ def test_invalid_ip(client): assert response.status_code == 400 +def test_valid_local_network_ip(client): + project = f.ProjectFactory() + f.ProjectModulesConfigFactory(project=project, config={ + "bitbucket": { + "secret": "tpnIwJDz4e", + "valid_origin_ips": ["192.168.1.1"] + } + }) + + url = reverse("bitbucket-hook-list") + url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") + data = {'payload': ['{"commits": []}']} + response = client.post(url, + urllib.parse.urlencode(data, True), + content_type="application/x-www-form-urlencoded", + REMOTE_ADDR="192.168.1.1") + assert response.status_code == 204 + + def test_not_ip_filter(client): project = f.ProjectFactory() f.ProjectModulesConfigFactory(project=project, config={ diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index c7b79c24..39aa5485 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -78,6 +78,26 @@ def test_invalid_ip(client): assert response.status_code == 400 +def test_valid_local_network_ip(client): + project = f.ProjectFactory() + f.ProjectModulesConfigFactory(project=project, config={ + "gitlab": { + "secret": "tpnIwJDz4e", + "valid_origin_ips": ["192.168.1.1"], + } + }) + + url = reverse("gitlab-hook-list") + url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") + data = {"test:": "data"} + response = client.post(url, + json.dumps(data), + content_type="application/json", + REMOTE_ADDR="192.168.1.1") + + assert response.status_code == 204 + + def test_not_ip_filter(client): project = f.ProjectFactory() f.ProjectModulesConfigFactory(project=project, config={ From 3e8c1814d5a4e557a13c13bb7d6f787627fb7473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 25 Jun 2015 15:46:48 +0200 Subject: [PATCH 029/190] Issue#2572: On status open/closed change recalc the is_closed for user stories --- taiga/projects/apps.py | 20 +++++++++++++++ taiga/projects/signals.py | 22 ++++++++++++++++ tests/integration/test_projects.py | 40 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/taiga/projects/apps.py b/taiga/projects/apps.py index a57f2f5a..c3ef4df3 100644 --- a/taiga/projects/apps.py +++ b/taiga/projects/apps.py @@ -53,6 +53,18 @@ def connect_projects_signals(): dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") +def connect_us_status_signals(): + signals.post_save.connect(handlers.try_to_close_or_open_user_stories_when_edit_us_status, + sender=apps.get_model("projects", "UserStoryStatus"), + dispatch_uid="try_to_close_or_open_user_stories_when_edit_us_status") + + +def connect_task_status_signals(): + signals.post_save.connect(handlers.try_to_close_or_open_user_stories_when_edit_task_status, + sender=apps.get_model("projects", "TaskStatus"), + dispatch_uid="try_to_close_or_open_user_stories_when_edit_task_status") + + def disconnect_memberships_signals(): signals.pre_delete.disconnect(dispatch_uid='membership_pre_delete') signals.post_delete.disconnect(dispatch_uid='update_watchers_on_membership_post_delete') @@ -64,6 +76,12 @@ def disconnect_projects_signals(): signals.pre_save.disconnect(dispatch_uid="tags_normalization_projects") signals.pre_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") +def disconnect_us_status_signals(): + signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_user_stories_when_edit_us_status") + +def disconnect_task_status_signals(): + signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_user_stories_when_edit_task_status") + class ProjectsAppConfig(AppConfig): name = "taiga.projects" @@ -72,3 +90,5 @@ class ProjectsAppConfig(AppConfig): def ready(self): connect_memberships_signals() connect_projects_signals() + connect_us_status_signals() + connect_task_status_signals() diff --git a/taiga/projects/signals.py b/taiga/projects/signals.py index 61dd0709..f6cb0b1a 100644 --- a/taiga/projects/signals.py +++ b/taiga/projects/signals.py @@ -97,3 +97,25 @@ def project_post_save(sender, instance, created, **kwargs): Membership = apps.get_model("projects", "Membership") Membership.objects.create(user=instance.owner, project=instance, role=owner_role, is_owner=True, email=instance.owner.email) + + +def try_to_close_or_open_user_stories_when_edit_us_status(sender, instance, created, **kwargs): + from taiga.projects.userstories import services + + for user_story in instance.user_stories.all(): + if services.calculate_userstory_is_closed(user_story): + services.close_userstory(user_story) + else: + services.open_userstory(user_story) + + +def try_to_close_or_open_user_stories_when_edit_task_status(sender, instance, created, **kwargs): + from taiga.projects.userstories import services + + UserStory = apps.get_model("userstories", "UserStory") + + for user_story in UserStory.objects.filter(tasks__status=instance).distinct(): + if services.calculate_userstory_is_closed(user_story): + services.close_userstory(user_story) + else: + services.open_userstory(user_story) diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 527b0d0e..45b7c96e 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -45,6 +45,46 @@ def test_partially_update_project(client): assert response.status_code == 400 +def test_us_status_is_closed_changed_recalc_us_is_closed(client): + us_status = f.UserStoryStatusFactory(is_closed=False) + user_story = f.UserStoryFactory.create(project=us_status.project, status=us_status) + + assert user_story.is_closed is False + + us_status.is_closed = True + us_status.save() + + user_story = user_story.__class__.objects.get(pk=user_story.pk) + assert user_story.is_closed is True + + us_status.is_closed = False + us_status.save() + + user_story = user_story.__class__.objects.get(pk=user_story.pk) + assert user_story.is_closed is False + + +def test_task_status_is_closed_changed_recalc_us_is_closed(client): + us_status = f.UserStoryStatusFactory() + user_story = f.UserStoryFactory.create(project=us_status.project, status=us_status) + task_status = f.TaskStatusFactory.create(project=us_status.project, is_closed=False) + task = f.TaskFactory.create(project=us_status.project, status=task_status, user_story=user_story) + + assert user_story.is_closed is False + + task_status.is_closed = True + task_status.save() + + user_story = user_story.__class__.objects.get(pk=user_story.pk) + assert user_story.is_closed is True + + task_status.is_closed = False + task_status.save() + + user_story = user_story.__class__.objects.get(pk=user_story.pk) + assert user_story.is_closed is False + + def test_us_status_slug_generation(client): us_status = f.UserStoryStatusFactory(name="NEW") f.MembershipFactory(user=us_status.project.owner, project=us_status.project, is_owner=True) From a7b9873e8dd63adc5e4fe3d964e038219e5fba05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 25 Jun 2015 18:08:12 +0200 Subject: [PATCH 030/190] [i18n] Update locales de --- taiga/locale/de/LC_MESSAGES/django.po | 229 +++++++++++++++++++++++++- 1 file changed, 228 insertions(+), 1 deletion(-) diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 9147444a..e8e499f3 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-24 10:12+0000\n" +"PO-Revision-Date: 2015-06-25 11:22+0000\n" "Last-Translator: Regina \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" @@ -434,6 +434,22 @@ msgid "" " \n" " " msgstr "" +"\n" +" Taiga Support:\n" +" " +"%(support_url)s\n" +"
\n" +" Kontaktieren Sie uns:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Mailing list:\n" +" \n" +" %(mailing_list_url)s\n" +" " #: taiga/base/templates/emails/hero-body-html.jinja:6 msgid "You have been Taigatized" @@ -914,6 +930,12 @@ msgid "" "\n" "{message}" msgstr "" +"Kommentar von [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") von GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" #: taiga/hooks/github/event_hooks.py:212 #, python-brace-format @@ -1693,6 +1715,14 @@ msgid "" "%(subject)s in Taiga\">See issue\n" " " msgstr "" +"\n" +"

Ticket aktualisiert

\n" +"

Hallo %(user)s,
%(changer)s hat ein Ticket aktualisiert in " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +"Ticket ansehen\n" +" " #: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 #, python-format @@ -1731,6 +1761,15 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Neues Ticket wurde erstellt

\n" +"

Hallo %(user)s,
%(changer)s hat ein neues Ticket erstellt in " +"%(project)s

\n" +"

Ticket #%(ref)s %(subject)s

\n" +"Ticket ansehen\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/issues/issue-create-body-text.jinja:1 #, python-format @@ -1772,6 +1811,13 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Ticket gelöscht

\n" +"

Hallo %(user)s,
%(changer)s hat ein Ticket gelöscht in %(project)s\n" +"

Ticket #%(ref)s %(subject)s

\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-text.jinja:1 #, python-format @@ -1816,6 +1862,14 @@ msgid "" "Taiga\">See sprint\n" " " msgstr "" +"\n" +"

Sprint wurde aktualisiert

\n" +"

Hallo %(user)s,
%(changer)s hat einen Sprint aktualisiert in " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"Sprint ansehen\n" +" " #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 #, python-format @@ -1825,6 +1879,11 @@ msgid "" "Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" "See sprint %(name)s at %(url)s\n" msgstr "" +"\n" +"Sprint aktualisiert\n" +"Hallo %(user)s, %(changer)s hat einen Sprint aktualisiert in %(project)s\n" +"Sprint ansehen %(name)s auf %(url)s \n" +"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 #, python-format @@ -1849,6 +1908,15 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Neuer Sprint wurde erstellt

\n" +"

Hallo %(user)s,
%(changer)s hat einen neuen Sprint erstellt in " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"See " +"sprint\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-text.jinja:1 #, python-format @@ -1861,6 +1929,14 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Neuer Sprint wurde erstellt\n" +"Hallo %(user)s, %(changer)s hat einen neuen Sprint erstellt in %(project)s\n" +"Sprint ansehen %(name)s at %(url)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +" \n" #: taiga/projects/notifications/templates/emails/milestones/milestone-create-subject.jinja:1 #, python-format @@ -1883,6 +1959,13 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Sprint wurde gelöscht

\n" +"

Hallo %(user)s,
%(changer)s hat einen Sprint gelöscht in " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-text.jinja:1 #, python-format @@ -1895,6 +1978,14 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Sprint wurde gelöscht\n" +"Hallo %(user)s, %(changer)s hat einen Sprint gelöscht in %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-delete-subject.jinja:1 #, python-format @@ -1964,6 +2055,15 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Neue Aufgabe wurde erstellt

\n" +"

Hallo %(user)s,
%(changer)s hat eine neue Aufgabe erstellt in " +"%(project)s

\n" +"

Aufgabe #%(ref)s %(subject)s

\n" +"Aufgabe ansehen\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/tasks/task-create-body-text.jinja:1 #, python-format @@ -2007,6 +2107,13 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Aufgabe wurde gelöscht

\n" +"

Hallo %(user)s,
%(changer)s hat eine Aufgabe gelöscht in " +"%(project)s

\n" +"

Aufgabe #%(ref)s %(subject)s

\n" +"

Das Taiga Team

\n" +" " #: taiga/projects/notifications/templates/emails/tasks/task-delete-body-text.jinja:1 #, python-format @@ -2019,6 +2126,14 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Aufgabe wurde gelöscht\n" +"Hallo %(user)s, %(changer)s hat eine Aufgabe gelöscht in %(project)s\n" +"Aufgabe #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +" \n" #: taiga/projects/notifications/templates/emails/tasks/task-delete-subject.jinja:1 #, python-format @@ -2026,6 +2141,9 @@ msgid "" "\n" "[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Hat die Aufgabe gelöscht #%(ref)s \"%(subject)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 #, python-format @@ -2039,6 +2157,15 @@ msgid "" "%(subject)s in Taiga\">See user story\n" " " msgstr "" +"\n" +"

User-Story wurde aktualisiert

\n" +"

Hallo %(user)s,
%(changer)s hat eine User-Story aktualisiert in " +"%(project)s

\n" +"

User-Story #%(ref)s %(subject)s

\n" +"User-Story ansehen\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 #, python-format @@ -2048,6 +2175,11 @@ msgid "" "Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" "See user story #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"User-Story wurde aktualisiert\n" +"Hallo %(user)s, %(changer)s hat eine User-Story aktualisiert in %(project)s\n" +"User-Story ansehen #%(ref)s %(subject)s auf %(url)s\n" +"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 #, python-format @@ -2055,6 +2187,8 @@ msgid "" "\n" "[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Aktualisierte die User-Story #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 #, python-format @@ -2069,6 +2203,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Neue User-Story wurde erstellt

\n" +"

Hallo %(user)s,
%(changer)s hat eine neue User-Story erstellt in " +"%(project)s

\n" +"

User-Story #%(ref)s %(subject)s

\n" +"User-Story ansehen\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-text.jinja:1 #, python-format @@ -2081,6 +2225,15 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Neue User-Story wurde erstellt\n" +"Hallo %(user)s, %(changer)s hat eine neue User-Story erstellt in " +"%(project)s\n" +"User-Story ansehen #%(ref)s %(subject)s auf %(url)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-create-subject.jinja:1 #, python-format @@ -2100,6 +2253,14 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

User-Story wurde gelöscht

\n" +"

Hallo %(user)s,
%(changer)s hat eine User-Story gelöscht in " +"%(project)s

\n" +"

User-Story #%(ref)s %(subject)s

\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-text.jinja:1 #, python-format @@ -2112,6 +2273,14 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"User-Story wurde gelöscht\n" +"Hallo %(user)s, %(changer)s hat eine User-Story gelöscht in %(project)s\n" +"User-Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-delete-subject.jinja:1 #, python-format @@ -2132,6 +2301,15 @@ msgid "" "\">See Wiki Page\n" " " msgstr "" +"\n" +"

Wiki Seite wurde aktualisiert

\n" +"

Hallo %(user)s,
%(changer)s hat eine Wiki Seite aktualisiert in " +"%(project)s

\n" +"

Wiki Seite %(page)s

\n" +"Wiki " +"Seite ansehen\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 #, python-format @@ -2143,6 +2321,13 @@ msgid "" "\n" "See wiki page %(page)s at %(url)s\n" msgstr "" +"\n" +"Wiki Seite wurde aktualisiert\n" +"\n" +"Hallo %(user)s, %(changer)s hat eine Wiki Seite aktualisiert in %(project)s\n" +"\n" +"Wiki Seite ansehen %(page)s auf %(url)s\n" +"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 #, python-format @@ -2164,6 +2349,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Neue Wiki Seite wurde erstellt

\n" +"

Hallo %(user)s,
%(changer)s hat eine neue Wiki Seite erstellt in " +"%(project)s

\n" +"

Wiki Seite %(page)s

\n" +"Wiki Seite " +"ansehen\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-text.jinja:1 #, python-format @@ -2178,6 +2373,17 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Neue Wiki Seite wurde erstellt\n" +"\n" +"Hallo %(user)s, %(changer)s hat eine neue Wiki Seite erstellt in " +"%(project)s\n" +"\n" +"Wiki Seite ansehen %(page)s auf %(url)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-create-subject.jinja:1 #, python-format @@ -2197,6 +2403,14 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Wiki Seite wurde gelöscht

\n" +"

Hallo %(user)s,
%(changer)s hat eine Wiki Seite gelöscht in " +"%(project)s

\n" +"

Wiki Seite %(page)s

\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-text.jinja:1 #, python-format @@ -2211,6 +2425,16 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Wiki Seite wurde gelöscht\n" +"\n" +"Hallo %(user)s, %(changer)s hat eine Wiki Seite gelöscht in %(project)s\n" +"\n" +"Wiki Seite %(page)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-subject.jinja:1 #, python-format @@ -2407,6 +2631,9 @@ msgid "" "\n" "[Taiga] Invitation to join to the project '%(project)s'\n" msgstr "" +"\n" +"[Taiga] Einladung zur Teilnahme am Projekt '%(project)s'\n" +"\n" #: taiga/projects/templates/emails/membership_notification-body-html.jinja:4 #, python-format From 07a48372e30291f8a37aeb237a0f659cb8d4f0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 24 Jun 2015 16:00:31 +0200 Subject: [PATCH 031/190] Add user info to the HistoryEntrySerializer --- taiga/projects/history/serializers.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/taiga/projects/history/serializers.py b/taiga/projects/history/serializers.py index 4834e504..2b5f4e00 100644 --- a/taiga/projects/history/serializers.py +++ b/taiga/projects/history/serializers.py @@ -17,17 +17,34 @@ from taiga.base.api import serializers from taiga.base.fields import JsonField, I18NJsonField +from taiga.users.services import get_photo_or_gravatar_url + from . import models + HISTORY_ENTRY_I18N_FIELDS=("points", "status", "severity", "priority", "type") + class HistoryEntrySerializer(serializers.ModelSerializer): diff = JsonField() snapshot = JsonField() values = I18NJsonField(i18n_fields=HISTORY_ENTRY_I18N_FIELDS) values_diff = I18NJsonField(i18n_fields=HISTORY_ENTRY_I18N_FIELDS) - user = JsonField() + user = serializers.SerializerMethodField("get_user") delete_comment_user = JsonField() class Meta: model = models.HistoryEntry + + def get_user(self, entry): + user = {"pk": None, "username": None, "name": None, "photo": None, "is_active": False} + user.update(entry.user) + + user["photo"] = get_photo_or_gravatar_url(entry.owner) + user["is_active"] = entry.owner.is_active + + if entry.owner.is_active or entry.owner.is_system: + user["name"] = entry.owner.get_full_name() + user["username"] = entry.owner.username + + return user From 45186558a7379df8f1df12b83ad89b051a035d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 26 Jun 2015 12:40:16 +0200 Subject: [PATCH 032/190] Added owner extra info to serializers --- taiga/projects/issues/serializers.py | 1 + taiga/projects/tasks/serializers.py | 1 + taiga/projects/userstories/serializers.py | 1 + 3 files changed, 3 insertions(+) diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index dd0d4ef5..77b22bc5 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -40,6 +40,7 @@ class IssueSerializer(WatchersValidator, serializers.ModelSerializer): votes = serializers.SerializerMethodField("get_votes_number") status_extra_info = BasicIssueStatusSerializer(source="status", required=False, read_only=True) assigned_to_extra_info = UserBasicInfoSerializer(source="assigned_to", required=False, read_only=True) + owner_extra_info = UserBasicInfoSerializer(source="owner", required=False, read_only=True) class Meta: model = models.Issue diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 30a63d1b..131bb75b 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -42,6 +42,7 @@ class TaskSerializer(WatchersValidator, serializers.ModelSerializer): is_closed = serializers.SerializerMethodField("get_is_closed") status_extra_info = BasicTaskStatusSerializerSerializer(source="status", required=False, read_only=True) assigned_to_extra_info = UserBasicInfoSerializer(source="assigned_to", required=False, read_only=True) + owner_extra_info = UserBasicInfoSerializer(source="owner", required=False, read_only=True) class Meta: model = models.Task diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 30129e7b..1773776f 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -55,6 +55,7 @@ class UserStorySerializer(WatchersValidator, serializers.ModelSerializer): description_html = serializers.SerializerMethodField("get_description_html") status_extra_info = BasicUserStoryStatusSerializer(source="status", required=False, read_only=True) assigned_to_extra_info = UserBasicInfoSerializer(source="assigned_to", required=False, read_only=True) + owner_extra_info = UserBasicInfoSerializer(source="owner", required=False, read_only=True) class Meta: model = models.UserStory From 2d2528608496bc319aa1bc9b472a64d30af25c0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 26 Jun 2015 12:45:48 +0200 Subject: [PATCH 033/190] Add is_active to UserBasicInfoSerializer --- taiga/users/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 1a22ff70..115462be 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -111,7 +111,7 @@ class UserAdminSerializer(UserSerializer): class UserBasicInfoSerializer(UserSerializer): class Meta: model = User - fields = ("username", "full_name_display","photo", "big_photo") + fields = ("username", "full_name_display","photo", "big_photo", "is_active") class RecoverySerializer(serializers.Serializer): From 6aea158762caecf0d6d6adae9327a73c0703a973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 29 Jun 2015 12:49:19 +0200 Subject: [PATCH 034/190] Issue#2966: Change text "milestone" by "sprint" in change emails --- taiga/projects/history/templatetags/functions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taiga/projects/history/templatetags/functions.py b/taiga/projects/history/templatetags/functions.py index eef5133b..8d616b73 100644 --- a/taiga/projects/history/templatetags/functions.py +++ b/taiga/projects/history/templatetags/functions.py @@ -24,7 +24,8 @@ register = library.Library() EXTRA_FIELD_VERBOSE_NAMES = { "description_diff": _("description"), "content_diff": _("content"), - "blocked_note_diff": _("blocked note") + "blocked_note_diff": _("blocked note"), + "milestone": _("sprint"), } From 00901a3c936f41f4adf771bc06c37f0ca6221b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 29 Jun 2015 14:46:03 +0200 Subject: [PATCH 035/190] [i18n] Update locales --- taiga/locale/de/LC_MESSAGES/django.po | 187 ++++++++++++++++++--- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 13 +- 2 files changed, 175 insertions(+), 25 deletions(-) diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index e8e499f3..12fedc19 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-25 11:22+0000\n" +"PO-Revision-Date: 2015-06-29 10:23+0000\n" "Last-Translator: Regina \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" @@ -349,7 +349,7 @@ msgstr "Fehler bei Datenüberprüfung " #: taiga/base/exceptions.py:162 msgid "Integrity Error for wrong or invalid arguments" -msgstr "" +msgstr "Integritätsfehler wegen falscher oder ungültiger Argumente" #: taiga/base/exceptions.py:169 msgid "Precondition error" @@ -614,6 +614,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Projekt Export Datei wurde erstellt

\n" +"

Hallo %(user)s,

\n" +"

Ihre Export Datei von Projekt %(project)s wurde korrekt erstellt.

\n" +"

Sie können sie hier herunterladen:

\n" +"Export " +"Datei herunterladen\n" +"

Diese Datei wird gelöscht am %(deletion_date)s.

\n" +"

Das Taiga Team

\n" +" " #: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 #, python-format @@ -631,6 +641,19 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Hallo %(user)s,\n" +"\n" +"Ihre Export Datei von Projekt %(project)s wurde korrekt erstellt. Sie können " +"sie hier herunterladen:\n" +"\n" +"%(url)s\n" +"\n" +"Diese Datei wird gelöscht am %(deletion_date)s.\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/export_import/templates/emails/dump_project-subject.jinja:1 #, python-format @@ -651,6 +674,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

%(error_message)s

\n" +"

Hallo %(user)s,

\n" +"

Ihr Projekt %(project)s wurde nicht korrekt importiert.

\n" +"

Die Taiga System Administratoren wurden informiert.
Bitte versuchen " +"Sie es erneut oder kontaktieren Sie das Support Team unter\n" +"%(support_email)s

\n" +"

Das Taiga Team

\n" +" " #: taiga/export_import/templates/emails/export_error-body-text.jinja:1 #, python-format @@ -668,6 +701,20 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Hallo %(user)s,\n" +"\n" +"%(error_message)s\n" +"Ihr Projekt %(project)s wurde nicht korrekt importiert.\n" +"\n" +"Die Taiga System Administratoren wurden informiert.\n" +"\n" +"Bitte versuchen Sie es erneut oder kontaktieren Sie das Support Team unter " +"%(support_email)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/export_import/templates/emails/export_error-subject.jinja:1 #, python-format @@ -688,6 +735,17 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

%(error_message)s

\n" +"

Hallo %(user)s,

\n" +"

Ihr Projekt wurde nicht korrekt importiert.

\n" +"

Die Taiga System Administratoren wurden informiert.
Bitte versuchen " +"Sie es erneut oder kontaktieren Sie das Support Team unter\n" +"%(support_email)s

\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/export_import/templates/emails/import_error-body-text.jinja:1 #, python-format @@ -738,6 +796,15 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Projekt Export-Datei wurde importiert

\n" +"

Hallo %(user)s,

\n" +"

Ihre Projekt Export-Datei wurde korrekt importiert.

\n" +"Gehe zu %(project)s\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/export_import/templates/emails/load_dump-body-text.jinja:1 #, python-format @@ -754,6 +821,18 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Hallo %(user)s,\n" +"\n" +"Ihre Projekt Export-Datei wurde korrekt importiert.\n" +"\n" +"Sie können das Projekt %(project)s hier sehen:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/export_import/templates/emails/load_dump-subject.jinja:1 #, python-format @@ -844,7 +923,7 @@ msgstr "" #: taiga/hooks/api.py:52 msgid "The payload is not a valid json" -msgstr "" +msgstr "Die Nutzlast ist kein gültiges json" #: taiga/hooks/api.py:61 msgid "The project doesn't exist" @@ -852,15 +931,15 @@ msgstr "Das Projekt existiert nicht" #: taiga/hooks/api.py:64 msgid "Bad signature" -msgstr "" +msgstr "Falsche Signatur" #: taiga/hooks/bitbucket/api.py:40 msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "" +msgstr "Die Nutzlast ist eine ungültige Anwendung/x-www-form-urlencoded" #: taiga/hooks/bitbucket/event_hooks.py:45 msgid "The payload is not valid" -msgstr "" +msgstr "Die Nutzlast ist ungültig" #: taiga/hooks/bitbucket/event_hooks.py:81 #: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 @@ -1604,7 +1683,7 @@ msgstr "Tag Farben" #: taiga/projects/models.py:339 msgid "modules config" -msgstr "" +msgstr "Module konfigurieren" #: taiga/projects/models.py:358 msgid "is archived" @@ -1745,7 +1824,7 @@ msgid "" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Aktualisierte das Ticket #%(ref)s \"%(subject)s\"\n" +"[%(project)s] aktualisierte das Ticket #%(ref)s \"%(subject)s\"\n" " \n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 @@ -1797,7 +1876,7 @@ msgid "" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Erstellte das Ticket #%(ref)s \"%(subject)s\"\n" +"[%(project)s] erstellte das Ticket #%(ref)s \"%(subject)s\"\n" "\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 @@ -1846,7 +1925,7 @@ msgid "" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Löschte das Ticket #%(ref)s \"%(subject)s\"\n" +"[%(project)s] löschte das Ticket #%(ref)s \"%(subject)s\"\n" " \n" "\n" @@ -1892,7 +1971,7 @@ msgid "" "[%(project)s] Updated the sprint \"%(milestone)s\"\n" msgstr "" "\n" -"[%(project)s] Aktualisierte den Sprint \"%(milestone)s\"\n" +"[%(project)s] aktualisierte den Sprint \"%(milestone)s\"\n" " \n" #: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 @@ -1945,7 +2024,7 @@ msgid "" "[%(project)s] Created the sprint \"%(milestone)s\"\n" msgstr "" "\n" -"[%(project)s] Erstellte den Sprint \"%(milestone)s\"\n" +"[%(project)s] erstellte den Sprint \"%(milestone)s\"\n" "\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 @@ -1994,7 +2073,7 @@ msgid "" "[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" msgstr "" "\n" -"[%(project)s] Löschte den Sprint \"%(milestone)s\"\n" +"[%(project)s] löschte den Sprint \"%(milestone)s\"\n" " \n" #: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 @@ -2040,7 +2119,7 @@ msgid "" "[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Aktualisierte die Aufgabe #%(ref)s \"%(subject)s\"\n" +"[%(project)s] aktualisierte die Aufgabe #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 #, python-format @@ -2093,7 +2172,7 @@ msgid "" "[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Erstellte die Aufgabe #%(ref)s \"%(subject)s\"\n" +"[%(project)s] erstellte die Aufgabe #%(ref)s \"%(subject)s\"\n" "\n" #: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 @@ -2142,7 +2221,7 @@ msgid "" "[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Hat die Aufgabe gelöscht #%(ref)s \"%(subject)s\"\n" +"[%(project)s] hat die Aufgabe gelöscht #%(ref)s \"%(subject)s\"\n" "\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 @@ -2188,7 +2267,7 @@ msgid "" "[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Aktualisierte die User-Story #%(ref)s \"%(subject)s\"\n" +"[%(project)s] aktualisierte die User-Story #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 #, python-format @@ -2241,6 +2320,9 @@ msgid "" "\n" "[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] erstellte die User-Story #%(ref)s \"%(subject)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 #, python-format @@ -2288,6 +2370,9 @@ msgid "" "\n" "[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] löschte die User-Story #%(ref)s \"%(subject)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 #, python-format @@ -2335,6 +2420,9 @@ msgid "" "\n" "[%(project)s] Updated the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] aktualisierte die Wiki Seite \"%(page)s\"\n" +" \n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 #, python-format @@ -2391,6 +2479,9 @@ msgid "" "\n" "[%(project)s] Created the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] erstetllte die Wiki Seite \"%(page)s\"\n" +"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 #, python-format @@ -2442,6 +2533,9 @@ msgid "" "\n" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] löschte die Wiki Seite \"%(page)s\"\n" +"\n" #: taiga/projects/notifications/validators.py:44 msgid "Watchers contains invalid users" @@ -2562,6 +2656,14 @@ msgid "" "Management Tool.

\n" " " msgstr "" +"\n" +"

Sie wurden zu Taiga eingeladen!

\n" +"

Hi! %(full_name)s hat Sie eingeladen, an folgendem Projekt in Taiga " +"teilzunehmen %(project)s \n" +"
Taiga ist ein kostenloses, quelloffenes, agiles Projekt Management " +"Tool.

\n" +"\n" +" " #: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 #, python-format @@ -2647,6 +2749,15 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Sie wurden einem Projekt hinzugefügt

\n" +"

Hallo %(full_name)s,
Sie wurden einem Projekt hinzugefügt " +"%(project)s

\n" +" Zum Projekt " +"gehen\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 #, python-format @@ -2657,6 +2768,11 @@ msgid "" "\n" "See project at %(url)s\n" msgstr "" +"\n" +"Sie wurden einem Projekt hinzugefügt\n" +"Hallo %(full_name)s, Sie wurden folgendem Projekt hinzugefügt %(project)s\n" +"\n" +"Projekt ansehen auf %(url)s\n" #: taiga/projects/templates/emails/membership_notification-subject.jinja:1 #, python-format @@ -2883,12 +2999,12 @@ msgstr "Design" #. Translators: User role #: taiga/projects/translations.py:174 msgid "Front" -msgstr "" +msgstr "Front" #. Translators: User role #: taiga/projects/translations.py:176 msgid "Back" -msgstr "" +msgstr "Back" #. Translators: User role #: taiga/projects/translations.py:178 @@ -3106,7 +3222,7 @@ msgstr "Vorgegebene Sprache" #: taiga/users/models.py:122 msgid "default theme" -msgstr "" +msgstr "Standard-Theme" #: taiga/users/models.py:124 msgid "default timezone" @@ -3152,6 +3268,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Änderung Ihrer E-Mail Adresse

\n" +"

Hallo %(full_name)s,
Bitte bestätigen Sie Ihre E-Mail Adresse

\n" +"E-Mail Adresse " +"bestätigen\n" +"

Sie können diese Nachricht ignorieren, wenn Sie keine Anfrage gestellt " +"haben

\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/users/templates/emails/change_email-body-text.jinja:1 #, python-format @@ -3199,6 +3325,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Passwort wiederherstellen

\n" +"

Hallo %(full_name)s,
Sie möchten Ihr Passwort wiederherstellen

\n" +"Passwort wiederherstellen\n" +"

Sie können diese Nachricht ignorieren, wenn Sie keine Anfrage gestellt " +"haben.

\n" +"

Das Taiga Team

\n" +"\n" +" " #: taiga/users/templates/emails/password_recovery-body-text.jinja:1 #, python-format @@ -3213,6 +3349,17 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Hallo %(full_name)s, Sie möchten Ihr Passwort wiederherstellen\n" +"\n" +"%(url)s\n" +"\n" +"Sie können diese Nachricht ignorieren, wenn Sie keine Anfrage gestellt " +"haben.\n" +"\n" +"---\n" +"Das Taiga Team\n" +"\n" #: taiga/users/templates/emails/password_recovery-subject.jinja:1 msgid "[Taiga] Password recovery" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 0969028b..7eae5e43 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-09 07:47+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-06-27 02:13+0000\n" +"Last-Translator: Chi-Hsun Tsai \n" "Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" @@ -469,6 +469,9 @@ msgid "" "%(comment)s

\n" " " msgstr "" +"\n" +"

評論:

\n" +"

%(comment)s

" #: taiga/base/templates/emails/updates-body-text.jinja:6 #, python-format @@ -482,7 +485,7 @@ msgstr "" #: taiga/export_import/api.py:103 msgid "We needed at least one role" -msgstr "" +msgstr "我們至少需要一個角色" #: taiga/export_import/api.py:197 msgid "Needed dump file" @@ -2473,7 +2476,7 @@ msgstr "版本須為整數值 " #: taiga/projects/occ/mixins.py:58 msgid "The version parameter is not valid" -msgstr "" +msgstr "本版本參數無效" #: taiga/projects/occ/mixins.py:74 msgid "The version doesn't match with the current one" @@ -3144,7 +3147,7 @@ msgstr "預設語言 " #: taiga/users/models.py:122 msgid "default theme" -msgstr "" +msgstr "預設主題" #: taiga/users/models.py:124 msgid "default timezone" From 83b8243d395ff73c0ddfd9491d7e25e59479fcb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 29 Jun 2015 16:05:42 +0200 Subject: [PATCH 036/190] Issue#2884: Crop images for the timeline --- settings/common.py | 2 ++ taiga/projects/history/freeze_impl.py | 14 ++++++++++++-- taiga/projects/history/models.py | 3 ++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/settings/common.py b/settings/common.py index a8c88c41..1a441e92 100644 --- a/settings/common.py +++ b/settings/common.py @@ -409,11 +409,13 @@ SOUTH_MIGRATION_MODULES = { DEFAULT_AVATAR_SIZE = 80 # 80x80 pixels DEFAULT_BIG_AVATAR_SIZE = 300 # 300x300 pixels +DEFAULT_TIMELINE_IMAGE_SIZE = 640 # 640x??? pixels THUMBNAIL_ALIASES = { '': { 'avatar': {'size': (DEFAULT_AVATAR_SIZE, DEFAULT_AVATAR_SIZE), 'crop': True}, 'big-avatar': {'size': (DEFAULT_BIG_AVATAR_SIZE, DEFAULT_BIG_AVATAR_SIZE), 'crop': True}, + 'timeline-image': {'size': (DEFAULT_TIMELINE_IMAGE_SIZE, 0), 'crop': True}, }, } diff --git a/taiga/projects/history/freeze_impl.py b/taiga/projects/history/freeze_impl.py index 1e1038cb..a591c666 100644 --- a/taiga/projects/history/freeze_impl.py +++ b/taiga/projects/history/freeze_impl.py @@ -21,6 +21,10 @@ from django.apps import apps from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist +from easy_thumbnails.files import get_thumbnailer +from easy_thumbnails.exceptions import InvalidImageFormatError + +from taiga.base.utils.urls import get_absolute_url from taiga.base.utils.iterators import as_tuple from taiga.base.utils.iterators import as_dict from taiga.mdrender.service import render as mdrender @@ -100,7 +104,7 @@ def project_values(diff): values = _common_users_values(diff) return values - + def milestone_values(diff): values = _common_users_values(diff) return values @@ -179,10 +183,16 @@ def _generic_extract(obj:object, fields:list, default=None) -> dict: @as_tuple def extract_attachments(obj) -> list: for attach in obj.attachments.all(): + try: + thumb_url = get_thumbnailer(attach.attached_file)['timeline-image'].url + thumb_url = get_absolute_url(thumb_url) + except InvalidImageFormatError as e: + thumb_url = None + yield {"id": attach.id, "filename": os.path.basename(attach.attached_file.name), "url": attach.attached_file.url, - "description": attach.description, + "thumb_url": thumb_url, "is_deprecated": attach.is_deprecated, "description": attach.description, "order": attach.order} diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 48b890fb..82d1667d 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -182,12 +182,13 @@ class HistoryEntry(models.Model): for aid in set(tuple(oldattachs.keys()) + tuple(newattachs.keys())): if aid in oldattachs and aid in newattachs: changes = make_diff_from_dicts(oldattachs[aid], newattachs[aid], - excluded_keys=("filename", "url")) + excluded_keys=("filename", "url", "thumb_url")) if changes: change = { "filename": newattachs.get(aid, {}).get("filename", ""), "url": newattachs.get(aid, {}).get("url", ""), + "thumb_url": newattachs.get(aid, {}).get("thumb_url", ""), "changes": changes } attachments["changed"].append(change) From 7ff2126b4914506c7aa93ce8e5bdb2cd4e5fc6b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 29 Jun 2015 17:58:31 +0200 Subject: [PATCH 037/190] Issue#2967: Don't delete tasks on milestone delete --- .../migrations/0007_auto_20150629_1556.py | 21 +++++++++++++++++++ taiga/projects/tasks/models.py | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 taiga/projects/tasks/migrations/0007_auto_20150629_1556.py diff --git a/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py new file mode 100644 index 00000000..e6596d7f --- /dev/null +++ b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('tasks', '0006_auto_20150623_1923'), + ] + + operations = [ + migrations.AlterField( + model_name='task', + name='milestone', + field=models.ForeignKey(to='milestones.Milestone', related_name='tasks', default=None, verbose_name='milestone', on_delete=django.db.models.deletion.SET_NULL, null=True, blank=True), + preserve_default=True, + ), + ] diff --git a/taiga/projects/tasks/models.py b/taiga/projects/tasks/models.py index 699321c0..37176fab 100644 --- a/taiga/projects/tasks/models.py +++ b/taiga/projects/tasks/models.py @@ -39,7 +39,7 @@ class Task(OCCModelMixin, WatchedModelMixin, BlockedMixin, TaggedMixin, models.M related_name="tasks", verbose_name=_("status")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="tasks", verbose_name=_("project")) - milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True, + milestone = models.ForeignKey("milestones.Milestone", null=True, blank=True, on_delete=models.SET_NULL, default=None, related_name="tasks", verbose_name=_("milestone")) created_date = models.DateTimeField(null=False, blank=False, From d4446cd4269269dd720761233344bceefab92bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 29 Jun 2015 18:13:33 +0200 Subject: [PATCH 038/190] Fix migration --- taiga/projects/tasks/migrations/0007_auto_20150629_1556.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py index e6596d7f..4bc08697 100644 --- a/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py +++ b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py @@ -8,7 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('tasks', '0006_auto_20150623_1923'), + ('tasks', '0005_auto_20150114_0954'), ] operations = [ From 9d94de9b219c2783673a18d517b36b33a222174c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 29 Jun 2015 18:57:08 +0200 Subject: [PATCH 039/190] Revert "Fix migration" This reverts commit d4446cd4269269dd720761233344bceefab92bd3. --- taiga/projects/tasks/migrations/0007_auto_20150629_1556.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py index 4bc08697..e6596d7f 100644 --- a/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py +++ b/taiga/projects/tasks/migrations/0007_auto_20150629_1556.py @@ -8,7 +8,7 @@ import django.db.models.deletion class Migration(migrations.Migration): dependencies = [ - ('tasks', '0005_auto_20150114_0954'), + ('tasks', '0006_auto_20150623_1923'), ] operations = [ From c80152599a6008bd9a0bcf15b151e4e8c7d609ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 15:07:46 +0200 Subject: [PATCH 040/190] Fix us auto closing test --- tests/integration/test_us_autoclosing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/test_us_autoclosing.py b/tests/integration/test_us_autoclosing.py index 35620026..e2205bb0 100644 --- a/tests/integration/test_us_autoclosing.py +++ b/tests/integration/test_us_autoclosing.py @@ -221,7 +221,7 @@ def test_auto_close_userstory_with_milestone_when_task_and_milestone_are_removed data.task3.delete() data.user_story1 = UserStory.objects.get(pk=data.user_story1.pk) - assert data.user_story1.is_closed is False + assert data.user_story1.is_closed is True def test_auto_close_us_when_all_tasks_are_changed_to_close_status(data): From 9769386b8ee501eeef526f4431bd54d50e4ba9fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 12:29:38 +0200 Subject: [PATCH 041/190] Issue#2981: Adapt taiga to the new bitbucket webhooks --- taiga/hooks/bitbucket/api.py | 15 +- taiga/hooks/bitbucket/event_hooks.py | 110 ++++++++- taiga/hooks/bitbucket/services.py | 15 +- tests/integration/test_hooks_bitbucket.py | 287 ++++++++++++++++++---- 4 files changed, 351 insertions(+), 76 deletions(-) diff --git a/taiga/hooks/bitbucket/api.py b/taiga/hooks/bitbucket/api.py index 82b71ea0..0b304ac8 100644 --- a/taiga/hooks/bitbucket/api.py +++ b/taiga/hooks/bitbucket/api.py @@ -29,18 +29,11 @@ from ipware.ip import get_ip class BitBucketViewSet(BaseWebhookApiViewSet): event_hook_classes = { - "push": event_hooks.PushEventHook, + "repo:push": event_hooks.PushEventHook, + "issue:created": event_hooks.IssuesEventHook, + "issue:comment_created": event_hooks.IssueCommentEventHook, } - def _get_payload(self, request): - try: - body = parse_qs(request.body.decode("utf-8"), strict_parsing=True) - payload = body["payload"] - except (ValueError, KeyError): - raise exc.BadRequest(_("The payload is not a valid application/x-www-form-urlencoded")) - - return payload - def _validate_signature(self, project, request): secret_key = request.GET.get("key", None) @@ -75,4 +68,4 @@ class BitBucketViewSet(BaseWebhookApiViewSet): return None def _get_event_name(self, request): - return "push" + return request.META.get('HTTP_X_EVENT_KEY', None) diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 969dae64..5aa86fde 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -37,17 +37,10 @@ class PushEventHook(BaseEventHook): if self.payload is None: return - # In bitbucket the payload is a list! :( - for payload_element_text in self.payload: - try: - payload_element = json.loads(payload_element_text) - except ValueError: - raise exc.BadRequest(_("The payload is not valid")) - - commits = payload_element.get("commits", []) - for commit in commits: - message = commit.get("message", None) - self._process_message(message, None) + changes = self.payload.get("push", {}).get('changes', []) + for change in changes: + message = change.get("new", {}).get("target", {}).get("message", None) + self._process_message(message, None) def _process_message(self, message, bitbucket_user): """ @@ -98,3 +91,98 @@ class PushEventHook(BaseEventHook): def replace_bitbucket_references(project_url, wiki_text): template = "\g<1>[BitBucket#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url) return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M) + + +class IssuesEventHook(BaseEventHook): + def process_event(self): + number = self.payload.get('issue', {}).get('id', None) + subject = self.payload.get('issue', {}).get('title', None) + + bitbucket_url = self.payload.get('issue', {}).get('links', {}).get('html', {}).get('href', None) + + bitbucket_user_id = self.payload.get('actor', {}).get('user', {}).get('uuid', None) + bitbucket_user_name = self.payload.get('actor', {}).get('user', {}).get('username', None) + bitbucket_user_url = self.payload.get('actor', {}).get('user', {}).get('links', {}).get('html', {}).get('href') + + project_url = self.payload.get('repository', {}).get('links', {}).get('html', {}).get('href', None) + + description = self.payload.get('issue', {}).get('content', {}).get('raw', '') + description = replace_bitbucket_references(project_url, description) + + user = get_bitbucket_user(bitbucket_user_id) + + if not all([subject, bitbucket_url, project_url]): + raise ActionSyntaxException(_("Invalid issue information")) + + issue = Issue.objects.create( + project=self.project, + subject=subject, + description=description, + status=self.project.default_issue_status, + type=self.project.default_issue_type, + severity=self.project.default_severity, + priority=self.project.default_priority, + external_reference=['bitbucket', bitbucket_url], + owner=user + ) + take_snapshot(issue, user=user) + + if number and subject and bitbucket_user_name and bitbucket_user_url: + comment = _("Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} " + "\"See @{bitbucket_user_name}'s BitBucket profile\") " + "from BitBucket.\nOrigin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} " + "\"Go to 'gh#{number} - {subject}'\"):\n\n" + "{description}").format(bitbucket_user_name=bitbucket_user_name, + bitbucket_user_url=bitbucket_user_url, + number=number, + subject=subject, + bitbucket_url=bitbucket_url, + description=description) + else: + comment = _("Issue created from BitBucket.") + + snapshot = take_snapshot(issue, comment=comment, user=user) + send_notifications(issue, history=snapshot) + + +class IssueCommentEventHook(BaseEventHook): + def process_event(self): + number = self.payload.get('issue', {}).get('id', None) + subject = self.payload.get('issue', {}).get('title', None) + + bitbucket_url = self.payload.get('issue', {}).get('links', {}).get('html', {}).get('href', None) + bitbucket_user_id = self.payload.get('actor', {}).get('user', {}).get('uuid', None) + bitbucket_user_name = self.payload.get('actor', {}).get('user', {}).get('username', None) + bitbucket_user_url = self.payload.get('actor', {}).get('user', {}).get('links', {}).get('html', {}).get('href') + + project_url = self.payload.get('repository', {}).get('links', {}).get('html', {}).get('href', None) + + comment_message = self.payload.get('comment', {}).get('content', {}).get('raw', '') + comment_message = replace_bitbucket_references(project_url, comment_message) + + user = get_bitbucket_user(bitbucket_user_id) + + if not all([comment_message, bitbucket_url, project_url]): + raise ActionSyntaxException(_("Invalid issue comment information")) + + issues = Issue.objects.filter(external_reference=["bitbucket", bitbucket_url]) + tasks = Task.objects.filter(external_reference=["bitbucket", bitbucket_url]) + uss = UserStory.objects.filter(external_reference=["bitbucket", bitbucket_url]) + + for item in list(issues) + list(tasks) + list(uss): + if number and subject and bitbucket_user_name and bitbucket_user_url: + comment = _("Comment by [@{bitbucket_user_name}]({bitbucket_user_url} " + "\"See @{bitbucket_user_name}'s BitBucket profile\") " + "from BitBucket.\nOrigin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} " + "\"Go to 'gh#{number} - {subject}'\")\n\n" + "{message}").format(bitbucket_user_name=bitbucket_user_name, + bitbucket_user_url=bitbucket_user_url, + number=number, + subject=subject, + bitbucket_url=bitbucket_url, + message=comment_message) + else: + comment = _("Comment From BitBucket:\n\n{message}").format(message=comment_message) + + snapshot = take_snapshot(item, comment=comment, user=user) + send_notifications(item, history=snapshot) diff --git a/taiga/hooks/bitbucket/services.py b/taiga/hooks/bitbucket/services.py index 625c91a8..ddd4af79 100644 --- a/taiga/hooks/bitbucket/services.py +++ b/taiga/hooks/bitbucket/services.py @@ -40,16 +40,5 @@ def get_or_generate_config(project): return g_config -def get_bitbucket_user(user_email): - user = None - - if user_email: - try: - user = User.objects.get(email=user_email) - except User.DoesNotExist: - pass - - if user is None: - user = User.objects.get(is_system=True, username__startswith="bitbucket") - - return user +def get_bitbucket_user(user_id): + return User.objects.get(is_system=True, username__startswith="bitbucket") diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index ecb4058d..27cf34db 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -14,6 +14,10 @@ from taiga.hooks.exceptions import ActionSyntaxException from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory +from taiga.projects.models import Membership +from taiga.projects.history.services import get_history_queryset_by_model_instance, take_snapshot +from taiga.projects.notifications.choices import NotifyLevel +from taiga.projects.notifications.models import NotifyPolicy from taiga.projects import services from .. import factories as f @@ -30,8 +34,9 @@ def test_bad_signature(client): url = reverse("bitbucket-hook-list") url = "{}?project={}&key={}".format(url, project.id, "badbadbad") - data = {} - response = client.post(url, urllib.parse.urlencode(data, True), content_type="application/x-www-form-urlencoded") + data = "{}" + response = client.post(url, data, content_type="application/json", HTTP_X_EVENT_KEY="repo:push") + response_content = response.data assert response.status_code == 400 assert "Bad signature" in response_content["_error_message"] @@ -47,10 +52,11 @@ def test_ok_signature(client): url = reverse("bitbucket-hook-list") url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") - data = {'payload': ['{"commits": []}']} + data = json.dumps({"push": {"changes": [{"new": {"target": { "message": "test message"}}}]}}) response = client.post(url, - urllib.parse.urlencode(data, True), - content_type="application/x-www-form-urlencoded", + data, + content_type="application/json", + HTTP_X_EVENT_KEY="repo:push", REMOTE_ADDR=settings.BITBUCKET_VALID_ORIGIN_IPS[0]) assert response.status_code == 204 @@ -65,10 +71,11 @@ def test_invalid_ip(client): url = reverse("bitbucket-hook-list") url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") - data = {'payload': ['{"commits": []}']} + data = json.dumps({"push": {"changes": [{"new": {"target": { "message": "test message"}}}]}}) response = client.post(url, - urllib.parse.urlencode(data, True), - content_type="application/x-www-form-urlencoded", + data, + content_type="application/json", + HTTP_X_EVENT_KEY="repo:push", REMOTE_ADDR="111.111.111.112") assert response.status_code == 400 @@ -84,10 +91,11 @@ def test_valid_local_network_ip(client): url = reverse("bitbucket-hook-list") url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") - data = {'payload': ['{"commits": []}']} + data = json.dumps({"push": {"changes": [{"new": {"target": { "message": "test message"}}}]}}) response = client.post(url, - urllib.parse.urlencode(data, True), - content_type="application/x-www-form-urlencoded", + data, + content_type="application/json", + HTTP_X_EVENT_KEY="repo:push", REMOTE_ADDR="192.168.1.1") assert response.status_code == 204 @@ -103,10 +111,11 @@ def test_not_ip_filter(client): url = reverse("bitbucket-hook-list") url = "{}?project={}&key={}".format(url, project.id, "tpnIwJDz4e") - data = {'payload': ['{"commits": []}']} + data = json.dumps({"push": {"changes": [{"new": {"target": { "message": "test message"}}}]}}) response = client.post(url, - urllib.parse.urlencode(data, True), - content_type="application/x-www-form-urlencoded", + data, + content_type="application/json", + HTTP_X_EVENT_KEY="repo:push", REMOTE_ADDR="111.111.111.112") assert response.status_code == 204 @@ -115,13 +124,14 @@ def test_push_event_detected(client): project = f.ProjectFactory() url = reverse("bitbucket-hook-list") url = "%s?project=%s" % (url, project.id) - data = {'payload': ['{"commits": [{"message": "test message"}]}']} + data = json.dumps({"push": {"changes": [{"new": {"target": { "message": "test message"}}}]}}) BitBucketViewSet._validate_signature = mock.Mock(return_value=True) with mock.patch.object(event_hooks.PushEventHook, "process_event") as process_event_mock: - response = client.post(url, urllib.parse.urlencode(data, True), - content_type="application/x-www-form-urlencoded") + response = client.post(url, data, + HTTP_X_EVENT_KEY="repo:push", + content_type="application/json") assert process_event_mock.call_count == 1 @@ -134,9 +144,7 @@ def test_push_event_issue_processing(client): f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) new_status = f.IssueStatusFactory(project=creation_status.project) issue = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) - payload = [ - '{"commits": [{"message": "test message test TG-%s #%s ok bye!"}]}' % (issue.ref, new_status.slug) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #%s ok bye!" % (issue.ref, new_status.slug)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(issue.project, payload) ev_hook.process_event() @@ -151,9 +159,7 @@ def test_push_event_task_processing(client): f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) new_status = f.TaskStatusFactory(project=creation_status.project) task = f.TaskFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) - payload = [ - '{"commits": [{"message": "test message test TG-%s #%s ok bye!"}]}' % (task.ref, new_status.slug) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #%s ok bye!" % (task.ref, new_status.slug)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(task.project, payload) ev_hook.process_event() @@ -168,9 +174,7 @@ def test_push_event_user_story_processing(client): f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) new_status = f.UserStoryStatusFactory(project=creation_status.project) user_story = f.UserStoryFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) - payload = [ - '{"commits": [{"message": "test message test TG-%s #%s ok bye!"}]}' % (user_story.ref, new_status.slug) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #%s ok bye!" % (user_story.ref, new_status.slug)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(user_story.project, payload) ev_hook.process_event() @@ -186,9 +190,7 @@ def test_push_event_multiple_actions(client): new_status = f.IssueStatusFactory(project=creation_status.project) issue1 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) issue2 = f.IssueFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) - payload = [ - '{"commits": [{"message": "test message test TG-%s #%s ok test TG-%s #%s ok bye!"}]}' % (issue1.ref, new_status.slug, issue2.ref, new_status.slug) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #%s ok test TG-%s #%s ok bye!" % (issue1.ref, new_status.slug, issue2.ref, new_status.slug)}}}]}} mail.outbox = [] ev_hook1 = event_hooks.PushEventHook(issue1.project, payload) ev_hook1.process_event() @@ -205,9 +207,7 @@ def test_push_event_processing_case_insensitive(client): f.MembershipFactory(project=creation_status.project, role=role, user=creation_status.project.owner) new_status = f.TaskStatusFactory(project=creation_status.project) task = f.TaskFactory.create(status=creation_status, project=creation_status.project, owner=creation_status.project.owner) - payload = [ - '{"commits": [{"message": "test message test tg-%s #%s ok bye!"}]}' % (task.ref, new_status.slug.upper()) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #%s ok bye!" % (task.ref, new_status.slug)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(task.project, payload) ev_hook.process_event() @@ -218,9 +218,7 @@ def test_push_event_processing_case_insensitive(client): def test_push_event_task_bad_processing_non_existing_ref(client): issue_status = f.IssueStatusFactory() - payload = [ - '{"commits": [{"message": "test message test TG-6666666 #%s ok bye!"}]}' % (issue_status.slug) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-6666666 #%s ok bye!" % (issue_status.slug)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(issue_status.project, payload) @@ -233,9 +231,7 @@ def test_push_event_task_bad_processing_non_existing_ref(client): def test_push_event_us_bad_processing_non_existing_status(client): user_story = f.UserStoryFactory.create() - payload = [ - '{"commits": [{"message": "test message test TG-%s #non-existing-slug ok bye!"}]}' % (user_story.ref) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #non-existing-slug ok bye!" % (user_story.ref)}}}]}} mail.outbox = [] @@ -249,9 +245,7 @@ def test_push_event_us_bad_processing_non_existing_status(client): def test_push_event_bad_processing_non_existing_status(client): issue = f.IssueFactory.create() - payload = [ - '{"commits": [{"message": "test message test TG-%s #non-existing-slug ok bye!"}]}' % (issue.ref) - ] + payload = {"push": {"changes": [{"new": {"target": { "message": "test message test TG-%s #non-existing-slug ok bye!" % (issue.ref)}}}]}} mail.outbox = [] ev_hook = event_hooks.PushEventHook(issue.project, payload) @@ -262,6 +256,217 @@ def test_push_event_bad_processing_non_existing_status(client): assert len(mail.outbox) == 0 +def test_issues_event_opened_issue(client): + issue = f.IssueFactory.create() + issue.project.default_issue_status = issue.status + issue.project.default_issue_type = issue.type + issue.project.default_severity = issue.severity + issue.project.default_priority = issue.priority + issue.project.save() + Membership.objects.create(user=issue.owner, project=issue.project, role=f.RoleFactory.create(project=issue.project), is_owner=True) + notify_policy = NotifyPolicy.objects.get(user=issue.owner, project=issue.project) + notify_policy.notify_level = NotifyLevel.watch + notify_policy.save() + + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "issue": { + "id": "10", + "title": "test-title", + "links": {"html": {"href": "http://bitbucket.com/site/master/issue/10"}}, + "content": {"raw": "test-content"} + }, + "repository": { + "links": {"html": {"href": "http://bitbucket.com/test-user/test-project"}} + } + } + + mail.outbox = [] + + ev_hook = event_hooks.IssuesEventHook(issue.project, payload) + ev_hook.process_event() + + assert Issue.objects.count() == 2 + assert len(mail.outbox) == 1 + + +def test_issues_event_bad_issue(client): + issue = f.IssueFactory.create() + issue.project.default_issue_status = issue.status + issue.project.default_issue_type = issue.type + issue.project.default_severity = issue.severity + issue.project.default_priority = issue.priority + issue.project.save() + + payload = { + "actor": { + }, + "issue": { + }, + "repository": { + } + } + mail.outbox = [] + + ev_hook = event_hooks.IssuesEventHook(issue.project, payload) + + with pytest.raises(ActionSyntaxException) as excinfo: + ev_hook.process_event() + + assert str(excinfo.value) == "Invalid issue information" + + assert Issue.objects.count() == 1 + assert len(mail.outbox) == 0 + + +def test_issue_comment_event_on_existing_issue_task_and_us(client): + project = f.ProjectFactory() + role = f.RoleFactory(project=project, permissions=["view_tasks", "view_issues", "view_us"]) + f.MembershipFactory(project=project, role=role, user=project.owner) + user = f.UserFactory() + + issue = f.IssueFactory.create(external_reference=["bitbucket", "http://bitbucket.com/site/master/issue/11"], owner=project.owner, project=project) + take_snapshot(issue, user=user) + task = f.TaskFactory.create(external_reference=["bitbucket", "http://bitbucket.com/site/master/issue/11"], owner=project.owner, project=project) + take_snapshot(task, user=user) + us = f.UserStoryFactory.create(external_reference=["bitbucket", "http://bitbucket.com/site/master/issue/11"], owner=project.owner, project=project) + take_snapshot(us, user=user) + + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "issue": { + "id": "11", + "title": "test-title", + "links": {"html": {"href": "http://bitbucket.com/site/master/issue/11"}}, + "content": {"raw": "test-content"} + }, + "comment": { + "content": {"raw": "Test body"}, + }, + "repository": { + "links": {"html": {"href": "http://bitbucket.com/test-user/test-project"}} + } + } + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + issue_history = get_history_queryset_by_model_instance(issue) + assert issue_history.count() == 1 + assert "Test body" in issue_history[0].comment + + task_history = get_history_queryset_by_model_instance(task) + assert task_history.count() == 1 + assert "Test body" in issue_history[0].comment + + us_history = get_history_queryset_by_model_instance(us) + assert us_history.count() == 1 + assert "Test body" in issue_history[0].comment + + assert len(mail.outbox) == 3 + + +def test_issue_comment_event_on_not_existing_issue_task_and_us(client): + issue = f.IssueFactory.create(external_reference=["bitbucket", "10"]) + take_snapshot(issue, user=issue.owner) + task = f.TaskFactory.create(project=issue.project, external_reference=["bitbucket", "10"]) + take_snapshot(task, user=task.owner) + us = f.UserStoryFactory.create(project=issue.project, external_reference=["bitbucket", "10"]) + take_snapshot(us, user=us.owner) + + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "issue": { + "id": "10", + "title": "test-title", + "links": {"html": {"href": "http://bitbucket.com/site/master/issue/10"}}, + "content": {"raw": "test-content"} + }, + "comment": { + "content": {"raw": "Test body"}, + }, + "repository": { + "links": {"html": {"href": "http://bitbucket.com/test-user/test-project"}} + } + } + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + assert len(mail.outbox) == 0 + + +def test_issues_event_bad_comment(client): + issue = f.IssueFactory.create(external_reference=["bitbucket", "10"]) + take_snapshot(issue, user=issue.owner) + + payload = { + "actor": { + "user": { + "uuid": "{ce1054cd-3f43-49dc-8aea-d3085ee7ec9b}", + "username": "test-user", + "links": {"html": {"href": "http://bitbucket.com/test-user"}} + } + }, + "issue": { + "id": "10", + "title": "test-title", + "links": {"html": {"href": "http://bitbucket.com/site/master/issue/10"}}, + "content": {"raw": "test-content"} + }, + "comment": { + }, + "repository": { + "links": {"html": {"href": "http://bitbucket.com/test-user/test-project"}} + } + } + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + + mail.outbox = [] + + with pytest.raises(ActionSyntaxException) as excinfo: + ev_hook.process_event() + + assert str(excinfo.value) == "Invalid issue comment information" + + assert Issue.objects.count() == 1 + assert len(mail.outbox) == 0 + + def test_api_get_project_modules(client): project = f.create_project() f.MembershipFactory(project=project, user=project.owner, is_owner=True) From 8b6fa19a5661745855ed452508b979e1cb1af94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 13:29:14 +0200 Subject: [PATCH 042/190] Issue#2981: Adapt taiga to the new bitbucket webhooks (Changelog) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99d9e63e..9798b058 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) - Allow multiple actions in the commit messages. - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). +- Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer From 8742f0cabb39ef6dce69681596a0ae5e740e89e8 Mon Sep 17 00:00:00 2001 From: Andrey Alekseenko Date: Tue, 30 Jun 2015 14:43:02 +0300 Subject: [PATCH 043/190] Using get_valid_filename to sanitize attachment names --- taiga/projects/attachments/models.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index c95c0f6b..5619bf68 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -27,16 +27,14 @@ from django.contrib.contenttypes import generic from django.utils import timezone from django.utils.encoding import force_bytes from django.utils.translation import ugettext_lazy as _ -from django.template.defaultfilters import slugify +from django.utils.text import get_valid_filename from taiga.base.utils.iterators import split_by_n def get_attachment_file_path(instance, filename): basename = path.basename(filename).lower() - base, ext = path.splitext(basename) - base = slugify(unidecode(base)) - basename = "".join([base, ext]) + basename = get_valid_filename(basename) hs = hashlib.sha256() hs.update(force_bytes(timezone.now().isoformat())) From eeaac88463f424b8cb8ef6feab87c2a18c5d3f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 20:08:57 +0200 Subject: [PATCH 044/190] Remove unnecesary lowecase of attachments filename --- taiga/projects/attachments/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index 5619bf68..61c590e4 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -33,7 +33,7 @@ from taiga.base.utils.iterators import split_by_n def get_attachment_file_path(instance, filename): - basename = path.basename(filename).lower() + basename = path.basename(filename) basename = get_valid_filename(basename) hs = hashlib.sha256() From 12b93b78a199542a7d2d1a4815b13c72ac31b47e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 20:34:56 +0200 Subject: [PATCH 045/190] Remove more unnecesary lowercase on attachments --- taiga/export_import/service.py | 2 +- taiga/projects/attachments/api.py | 2 +- taiga/projects/management/commands/sample_data.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index bd3049b9..1ca76eb0 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -276,7 +276,7 @@ def store_attachment(project, obj, attachment): serialized.object.owner = serialized.object.project.owner serialized.object._importing = True serialized.object.size = serialized.object.attached_file.size - serialized.object.name = path.basename(serialized.object.attached_file.name).lower() + serialized.object.name = path.basename(serialized.object.attached_file.name) serialized.save() return serialized add_errors("attachments", serialized.errors) diff --git a/taiga/projects/attachments/api.py b/taiga/projects/attachments/api.py index 0d26a0a8..6018433a 100644 --- a/taiga/projects/attachments/api.py +++ b/taiga/projects/attachments/api.py @@ -56,7 +56,7 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCru obj.content_type = self.get_content_type() obj.owner = self.request.user obj.size = obj.attached_file.size - obj.name = path.basename(obj.attached_file.name).lower() + obj.name = path.basename(obj.attached_file.name) if obj.project_id != obj.content_object.project_id: raise exc.WrongArguments(_("Project ID not matches between object and project")) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 96b3c47d..3fca91db 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -221,7 +221,7 @@ class Command(BaseCommand): membership = self.sd.db_object_from_queryset(obj.project.memberships .filter(user__isnull=False)) attachment = Attachment.objects.create(project=obj.project, - name=path.basename(attached_file.name).lower(), + name=path.basename(attached_file.name), size=attached_file.size, content_object=obj, order=order, From 16f39c8b7306731edad674cc0e1ce43ac1933e5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 30 Jun 2015 20:39:50 +0200 Subject: [PATCH 046/190] Add @al42and to AUTHORS :dancer: --- AUTHORS.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 896b7876..95154a8a 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -7,21 +7,21 @@ The PRIMARY AUTHORS are: - Xavi Julian - Anler Hernández -Special thanks to Kaleidos Open Source S.L. for provice time for taiga +Special thanks to Kaleidos Open Source S.L. for provice time for Taiga development. And here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS -- people who have submitted patches, reported bugs, added translations, helped answer newbie questions, and generally made taiga that much better: -- Andrés Moya -- Yamila Moreno -- Ricky Posner -- Alonso Torres - Alejandro Gómez +- Alonso Torres - Andrea Stagi -- Hector Colina -- Julien Palard -- Joe Letts +- Andrés Moya +- Andrey Alekseenko - Chris Wilson - +- Hector Colina +- Joe Letts +- Julien Palard +- Ricky Posner +- Yamila Moreno From bc2d8ece7f7c0c5eb1ad87284fda424de894e16a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 2 Jul 2015 00:40:51 +0200 Subject: [PATCH 047/190] Update regenerate.sh - Remove unnecessary commands --- regenerate.sh | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/regenerate.sh b/regenerate.sh index 0efc51a4..47f9c962 100755 --- a/regenerate.sh +++ b/regenerate.sh @@ -6,16 +6,12 @@ dropdb taiga echo "-> Create taiga DB" createdb taiga -echo "-> Run syncdb" +echo "-> Load migrations" python manage.py migrate -# echo "-> Load initial Site" -# python manage.py loaddata initial_site --traceback -echo "-> Load initial user" +echo "-> Load initial user (admin/123123)" python manage.py loaddata initial_user --traceback -echo "-> Load initial project_templates" +echo "-> Load initial project_templates (scrum/kanban)" 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 "-> Rebuilding timeline" From 03ff40ddd400b3da48790348842ad1f52dd64817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 2 Jul 2015 11:33:26 +0200 Subject: [PATCH 048/190] Add system-stats endpoint --- settings/common.py | 4 ++++ settings/local.py.example | 7 ++++++ taiga/routers.py | 7 +++++- taiga/stats/__init__.py | 15 +++++++++++++ taiga/stats/api.py | 33 ++++++++++++++++++++++++++++ taiga/stats/apps.py | 30 ++++++++++++++++++++++++++ taiga/stats/permissions.py | 20 +++++++++++++++++ taiga/stats/routers.py | 20 +++++++++++++++++ taiga/stats/services.py | 44 ++++++++++++++++++++++++++++++++++++++ 9 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 taiga/stats/__init__.py create mode 100644 taiga/stats/api.py create mode 100644 taiga/stats/apps.py create mode 100644 taiga/stats/permissions.py create mode 100644 taiga/stats/routers.py create mode 100644 taiga/stats/services.py diff --git a/settings/common.py b/settings/common.py index 1a441e92..716760aa 100644 --- a/settings/common.py +++ b/settings/common.py @@ -283,6 +283,7 @@ INSTALLED_APPS = [ "taiga.mdrender", "taiga.export_import", "taiga.feedback", + "taiga.stats", "taiga.hooks.github", "taiga.hooks.gitlab", "taiga.hooks.bitbucket", @@ -434,6 +435,9 @@ TAGS_PREDEFINED_COLORS = ["#fce94f", "#edd400", "#c4a000", "#8ae234", FEEDBACK_ENABLED = True FEEDBACK_EMAIL = "support@taiga.io" +# Stats module settings +STATS_ENABLED = False + # 0 notifications will work in a synchronous way # >0 an external process will check the pending notifications and will send them # collapsed during that interval diff --git a/settings/local.py.example b/settings/local.py.example index a74baa27..dd4ce8c9 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -63,6 +63,13 @@ DATABASES = { #GITHUB_API_CLIENT_ID = "yourgithubclientid" #GITHUB_API_CLIENT_SECRET = "yourgithubclientsecret" +# FEEDBACK MODULE (See config in taiga-front too) +#FEEDBACK_ENABLED = True +#FEEDBACK_EMAIL = "support@taiga.io" + +# STATS MODULE +#STATS_ENABLED = False + # SITEMAP # If is True /front/sitemap.xml show a valid sitemap of taiga-front client #FRONT_SITEMAP_ENABLED = False diff --git a/taiga/routers.py b/taiga/routers.py index 968c6354..0f8bc675 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -1,3 +1,4 @@ + # Copyright (C) 2014 Andrey Antukh # Copyright (C) 2014 Jesús Espino # Copyright (C) 2014 David Barragán @@ -192,5 +193,9 @@ router.register(r"importer", ProjectImporterViewSet, base_name="importer") router.register(r"exporter", ProjectExporterViewSet, base_name="exporter") -# feedback +# Stats +# - see taiga.stats.routers and taiga.stats.apps + + +# Feedback # - see taiga.feedback.routers and taiga.feedback.apps diff --git a/taiga/stats/__init__.py b/taiga/stats/__init__.py new file mode 100644 index 00000000..92ed9d3d --- /dev/null +++ b/taiga/stats/__init__.py @@ -0,0 +1,15 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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 . + +default_app_config = "taiga.stats.apps.StatsAppConfig" diff --git a/taiga/stats/api.py b/taiga/stats/api.py new file mode 100644 index 00000000..dae5cfdd --- /dev/null +++ b/taiga/stats/api.py @@ -0,0 +1,33 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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.base.api import viewsets +from taiga.base import response + +from . import permissions +from . import services + + +class SystemStatsViewSet(viewsets.ViewSet): + permission_classes = (permissions.SystemStatsPermission,) + + def list(self, request, **kwargs): + stats = { + "total_users": services.get_total_users(), + "total_projects": services.get_total_projects(), + "total_userstories": services.grt_total_user_stories(), + "total_issues": services.get_total_issues(), + } + return response.Ok(stats) diff --git a/taiga/stats/apps.py b/taiga/stats/apps.py new file mode 100644 index 00000000..e8b8e63a --- /dev/null +++ b/taiga/stats/apps.py @@ -0,0 +1,30 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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 django.apps import AppConfig +from django.apps import apps +from django.conf import settings +from django.conf.urls import include, url + +from .routers import router + + +class StatsAppConfig(AppConfig): + name = "taiga.stats" + verbose_name = "Stats" + + def ready(self): + if settings.STATS_ENABLED: + from taiga.urls import urlpatterns + urlpatterns.append(url(r'^api/v1/', include(router.urls))) diff --git a/taiga/stats/permissions.py b/taiga/stats/permissions.py new file mode 100644 index 00000000..0eb1a362 --- /dev/null +++ b/taiga/stats/permissions.py @@ -0,0 +1,20 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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.base.api import permissions + + +class SystemStatsPermission(permissions.TaigaResourcePermission): + global_perms = permissions.AllowAny() diff --git a/taiga/stats/routers.py b/taiga/stats/routers.py new file mode 100644 index 00000000..7257b473 --- /dev/null +++ b/taiga/stats/routers.py @@ -0,0 +1,20 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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.base import routers +from . import api + + +router = routers.DefaultRouter(trailing_slash=False) +router.register(r"stats/system", api.SystemStatsViewSet, base_name="system-stats") diff --git a/taiga/stats/services.py b/taiga/stats/services.py new file mode 100644 index 00000000..11f41d37 --- /dev/null +++ b/taiga/stats/services.py @@ -0,0 +1,44 @@ +# Copyright (C) 2015 Taiga Agile LLC +# 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 django.apps import apps + + +def get_total_projects(): + model = apps.get_model("projects", "Project") + queryset = model.objects.all() + return queryset.count() + + +def grt_total_user_stories(): + model = apps.get_model("userstories", "UserStory") + queryset = model.objects.all() + return queryset.count() + + +def get_total_issues(): + model = apps.get_model("issues", "Issue") + queryset = model.objects.all() + return queryset.count() + + +def get_total_users(only_active=True, no_system=True): + model = apps.get_model("users", "User") + queryset = model.objects.all() + if only_active: + queryset = queryset.filter(is_active=True) + if no_system: + queryset = queryset.filter(is_system=False) + return queryset.count() From 69d1ed91a3b7838e3e90cc245b9e3783f620b120 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 2 Jul 2015 11:33:42 +0200 Subject: [PATCH 049/190] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9798b058..8798a560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer +- API: Add stats/system resource with global server stats (total project, total users....) - Lots of small and not so small bugfixes. From ba828b4aa2ebd006d64a6af42df740cdd6ae2fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 1 Jul 2015 11:13:03 +0200 Subject: [PATCH 050/190] Issue#2995: Add custom video conference system --- CHANGELOG.md | 1 + taiga/projects/choices.py | 1 + .../fixtures/initial_project_templates.json | 4 +-- .../migrations/0022_auto_20150701_0924.py | 36 +++++++++++++++++++ taiga/projects/models.py | 14 ++++---- 5 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 taiga/projects/migrations/0022_auto_20150701_0924.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8798a560..924f2ebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Allow multiple actions in the commit messages. - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. +- Add custom videoconference system. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer diff --git a/taiga/projects/choices.py b/taiga/projects/choices.py index 9447898d..4cf2c843 100644 --- a/taiga/projects/choices.py +++ b/taiga/projects/choices.py @@ -20,5 +20,6 @@ from django.utils.translation import ugettext_lazy as _ VIDEOCONFERENCES_CHOICES = ( ("appear-in", _("AppearIn")), ("jitsi", _("Jitsi")), + ("custom", _("Custom")), ("talky", _("Talky")), ) diff --git a/taiga/projects/fixtures/initial_project_templates.json b/taiga/projects/fixtures/initial_project_templates.json index f0c2ebbb..46d369a5 100644 --- a/taiga/projects/fixtures/initial_project_templates.json +++ b/taiga/projects/fixtures/initial_project_templates.json @@ -16,7 +16,7 @@ "created_date": "2014-04-22T14:48:43.596Z", "default_options": "{\"us_status\": \"New\", \"task_status\": \"New\", \"priority\": \"Normal\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"points\": \"?\", \"issue_status\": \"New\"}", "slug": "scrum", - "videoconferences_salt": "", + "videoconferences_extra_data": "", "issue_statuses": "[{\"color\": \"#8C2318\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#5E8C6A\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#88A65E\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#BFB35A\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#89BAB4\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}, {\"color\": \"#CC0000\", \"order\": 6, \"is_closed\": true, \"name\": \"Rejected\", \"slug\": \"rejected\"}, {\"color\": \"#666666\", \"order\": 7, \"is_closed\": false, \"name\": \"Postponed\", \"slug\": \"posponed\"}]", "default_owner_role": "product-owner", "issue_types": "[{\"color\": \"#89BAB4\", \"order\": 1, \"name\": \"Bug\"}, {\"color\": \"#ba89a8\", \"order\": 2, \"name\": \"Question\"}, {\"color\": \"#89a8ba\", \"order\": 3, \"name\": \"Enhancement\"}]", @@ -43,7 +43,7 @@ "created_date": "2014-04-22T14:50:19.738Z", "default_options": "{\"us_status\": \"New\", \"task_status\": \"New\", \"priority\": \"Normal\", \"issue_type\": \"Bug\", \"severity\": \"Normal\", \"points\": \"?\", \"issue_status\": \"New\"}", "slug": "kanban", - "videoconferences_salt": "", + "videoconferences_extra_data": "", "issue_statuses": "[{\"color\": \"#999999\", \"order\": 1, \"is_closed\": false, \"name\": \"New\", \"slug\": \"new\"}, {\"color\": \"#729fcf\", \"order\": 2, \"is_closed\": false, \"name\": \"In progress\", \"slug\": \"in-progress\"}, {\"color\": \"#f57900\", \"order\": 3, \"is_closed\": true, \"name\": \"Ready for test\", \"slug\": \"ready-for-test\"}, {\"color\": \"#4e9a06\", \"order\": 4, \"is_closed\": true, \"name\": \"Closed\", \"slug\": \"closed\"}, {\"color\": \"#cc0000\", \"order\": 5, \"is_closed\": false, \"name\": \"Needs Info\", \"slug\": \"needs-info\"}, {\"color\": \"#d3d7cf\", \"order\": 6, \"is_closed\": true, \"name\": \"Rejected\", \"slug\": \"rejected\"}, {\"color\": \"#75507b\", \"order\": 7, \"is_closed\": false, \"name\": \"Postponed\", \"slug\": \"posponed\"}]", "default_owner_role": "product-owner", "issue_types": "[{\"color\": \"#cc0000\", \"order\": 1, \"name\": \"Bug\"}, {\"color\": \"#729fcf\", \"order\": 2, \"name\": \"Question\"}, {\"color\": \"#4e9a06\", \"order\": 3, \"name\": \"Enhancement\"}]", diff --git a/taiga/projects/migrations/0022_auto_20150701_0924.py b/taiga/projects/migrations/0022_auto_20150701_0924.py new file mode 100644 index 00000000..83d7a337 --- /dev/null +++ b/taiga/projects/migrations/0022_auto_20150701_0924.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0021_auto_20150504_1524'), + ] + + operations = [ + migrations.RenameField( + model_name='projecttemplate', + old_name='videoconferences_salt', + new_name='videoconferences_extra_data', + ), + migrations.RenameField( + model_name='project', + old_name='videoconferences_salt', + new_name='videoconferences_extra_data', + ), + migrations.AlterField( + model_name='project', + name='videoconferences', + field=models.CharField(blank=True, verbose_name='videoconference system', choices=[('appear-in', 'AppearIn'), ('jitsi', 'Jitsi'), ('custom', 'Custom'), ('talky', 'Talky')], null=True, max_length=250), + preserve_default=True, + ), + migrations.AlterField( + model_name='projecttemplate', + name='videoconferences', + field=models.CharField(blank=True, verbose_name='videoconference system', choices=[('appear-in', 'AppearIn'), ('jitsi', 'Jitsi'), ('custom', 'Custom'), ('talky', 'Talky')], null=True, max_length=250), + preserve_default=True, + ), + ] diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 64fff2a9..145edfb0 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -150,8 +150,8 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): 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")) + videoconferences_extra_data = models.CharField(max_length=250, null=True, blank=True, + verbose_name=_("videoconference extra data")) creation_template = models.ForeignKey("projects.ProjectTemplate", related_name="projects", null=True, @@ -209,7 +209,7 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): self.slug = slug if not self.videoconferences: - self.videoconferences_salt = None + self.videoconferences_extra_data = None super().save(*args, **kwargs) @@ -577,8 +577,8 @@ class ProjectTemplate(models.Model): 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")) + videoconferences_extra_data = models.CharField(max_length=250, null=True, blank=True, + verbose_name=_("videoconference extra data")) default_options = JsonField(null=True, blank=True, verbose_name=_("default options")) us_statuses = JsonField(null=True, blank=True, verbose_name=_("us statuses")) @@ -613,7 +613,7 @@ class ProjectTemplate(models.Model): 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.videoconferences_extra_data = project.videoconferences_extra_data self.default_options = { "points": getattr(project.default_points, "name", None), @@ -717,7 +717,7 @@ class ProjectTemplate(models.Model): 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 + project.videoconferences_extra_data = self.videoconferences_extra_data for us_status in self.us_statuses: UserStoryStatus.objects.create( From 3bae896199d260188e087ea65d3d29157e7798c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Tue, 30 Jun 2015 17:30:06 +0200 Subject: [PATCH 051/190] Add gitlab integration with commets webhook --- CHANGELOG.md | 1 + taiga/hooks/gitlab/api.py | 1 + taiga/hooks/gitlab/event_hooks.py | 47 ++++++++- tests/integration/test_hooks_gitlab.py | 129 +++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 924f2ebb..d22da965 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. - Add custom videoconference system. +- Add support for comments in the Gitlab webhooks integration. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer diff --git a/taiga/hooks/gitlab/api.py b/taiga/hooks/gitlab/api.py index 48d70fe7..03ae3a06 100644 --- a/taiga/hooks/gitlab/api.py +++ b/taiga/hooks/gitlab/api.py @@ -30,6 +30,7 @@ class GitLabViewSet(BaseWebhookApiViewSet): event_hook_classes = { "push": event_hooks.PushEventHook, "issue": event_hooks.IssuesEventHook, + "note": event_hooks.IssueCommentEventHook, } def _validate_signature(self, project, request): diff --git a/taiga/hooks/gitlab/event_hooks.py b/taiga/hooks/gitlab/event_hooks.py index 84079121..fdc83066 100644 --- a/taiga/hooks/gitlab/event_hooks.py +++ b/taiga/hooks/gitlab/event_hooks.py @@ -89,7 +89,7 @@ class PushEventHook(BaseEventHook): def replace_gitlab_references(project_url, wiki_text): - if wiki_text == None: + if wiki_text is None: wiki_text = "" template = "\g<1>[GitLab#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url) @@ -127,3 +127,48 @@ class IssuesEventHook(BaseEventHook): snapshot = take_snapshot(issue, comment=_("Created from GitLab"), user=get_gitlab_user(None)) send_notifications(issue, history=snapshot) + + +class IssueCommentEventHook(BaseEventHook): + def process_event(self): + if self.payload.get('object_attributes', {}).get("noteable_type", None) != "Issue": + return + + number = self.payload.get('issue', {}).get('iid', None) + subject = self.payload.get('issue', {}).get('title', None) + + project_url = self.payload.get('repository', {}).get('homepage', None) + + gitlab_url = os.path.join(project_url, "issues", str(number)) + gitlab_user_name = self.payload.get('user', {}).get('username', None) + gitlab_user_url = os.path.join(os.path.dirname(os.path.dirname(project_url)), "u", gitlab_user_name) + + comment_message = self.payload.get('object_attributes', {}).get('note', None) + comment_message = replace_gitlab_references(project_url, comment_message) + + user = get_gitlab_user(None) + + if not all([comment_message, gitlab_url, project_url]): + raise ActionSyntaxException(_("Invalid issue comment information")) + + issues = Issue.objects.filter(external_reference=["gitlab", gitlab_url]) + tasks = Task.objects.filter(external_reference=["gitlab", gitlab_url]) + uss = UserStory.objects.filter(external_reference=["gitlab", gitlab_url]) + + for item in list(issues) + list(tasks) + list(uss): + if number and subject and gitlab_user_name and gitlab_user_url: + comment = _("Comment by [@{gitlab_user_name}]({gitlab_user_url} " + "\"See @{gitlab_user_name}'s GitLab profile\") " + "from GitLab.\nOrigin GitLab issue: [gh#{number} - {subject}]({gitlab_url} " + "\"Go to 'gh#{number} - {subject}'\")\n\n" + "{message}").format(gitlab_user_name=gitlab_user_name, + gitlab_user_url=gitlab_user_url, + number=number, + subject=subject, + gitlab_url=gitlab_url, + message=comment_message) + else: + comment = _("Comment From GitLab:\n\n{message}").format(message=comment_message) + + snapshot = take_snapshot(item, comment=comment, user=user) + send_notifications(item, history=snapshot) diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index 39aa5485..eac03668 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -13,6 +13,7 @@ from taiga.projects.issues.models import Issue from taiga.projects.tasks.models import Task from taiga.projects.userstories.models import UserStory from taiga.projects.models import Membership +from taiga.projects.history.services import get_history_queryset_by_model_instance, take_snapshot from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.models import NotifyPolicy from taiga.projects import services @@ -384,6 +385,134 @@ def test_issues_event_bad_issue(client): assert len(mail.outbox) == 0 +def test_issue_comment_event_on_existing_issue_task_and_us(client): + project = f.ProjectFactory() + role = f.RoleFactory(project=project, permissions=["view_tasks", "view_issues", "view_us"]) + f.MembershipFactory(project=project, role=role, user=project.owner) + user = f.UserFactory() + + issue = f.IssueFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(issue, user=user) + task = f.TaskFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(task, user=user) + us = f.UserStoryFactory.create(external_reference=["gitlab", "http://gitlab.com/test/project/issues/11"], owner=project.owner, project=project) + take_snapshot(us, user=user) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "11", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + "note": "Test body", + }, + "repository": { + "homepage": "http://gitlab.com/test/project", + }, + } + + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + issue_history = get_history_queryset_by_model_instance(issue) + assert issue_history.count() == 1 + assert "Test body" in issue_history[0].comment + + task_history = get_history_queryset_by_model_instance(task) + assert task_history.count() == 1 + assert "Test body" in issue_history[0].comment + + us_history = get_history_queryset_by_model_instance(us) + assert us_history.count() == 1 + assert "Test body" in issue_history[0].comment + + assert len(mail.outbox) == 3 + + +def test_issue_comment_event_on_not_existing_issue_task_and_us(client): + issue = f.IssueFactory.create(external_reference=["gitlab", "10"]) + take_snapshot(issue, user=issue.owner) + task = f.TaskFactory.create(project=issue.project, external_reference=["gitlab", "10"]) + take_snapshot(task, user=task.owner) + us = f.UserStoryFactory.create(project=issue.project, external_reference=["gitlab", "10"]) + take_snapshot(us, user=us.owner) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "99999", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + "note": "test comment", + }, + "repository": { + "homepage": "test", + }, + } + + mail.outbox = [] + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + ev_hook.process_event() + + assert get_history_queryset_by_model_instance(issue).count() == 0 + assert get_history_queryset_by_model_instance(task).count() == 0 + assert get_history_queryset_by_model_instance(us).count() == 0 + + assert len(mail.outbox) == 0 + + +def test_issues_event_bad_comment(client): + issue = f.IssueFactory.create(external_reference=["gitlab", "10"]) + take_snapshot(issue, user=issue.owner) + + payload = { + "user": { + "username": "test" + }, + "issue": { + "iid": "10", + "title": "test-title", + }, + "object_attributes": { + "noteable_type": "Issue", + }, + "repository": { + "homepage": "test", + }, + } + ev_hook = event_hooks.IssueCommentEventHook(issue.project, payload) + + mail.outbox = [] + + with pytest.raises(ActionSyntaxException) as excinfo: + ev_hook.process_event() + + assert str(excinfo.value) == "Invalid issue comment information" + + assert Issue.objects.count() == 1 + assert len(mail.outbox) == 0 + + def test_api_get_project_modules(client): project = f.create_project() f.MembershipFactory(project=project, user=project.owner, is_owner=True) From 33a00657137fdc2f455a5467b7a4de3e962132bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 3 Jul 2015 14:55:34 +0200 Subject: [PATCH 052/190] Update gitlab user logo --- .../migrations/0002_auto_20150703_1102.py | 50 ++++++++++++++++++ taiga/hooks/gitlab/migrations/logo-v2.png | Bin 0 -> 37963 bytes 2 files changed, 50 insertions(+) create mode 100644 taiga/hooks/gitlab/migrations/0002_auto_20150703_1102.py create mode 100644 taiga/hooks/gitlab/migrations/logo-v2.png diff --git a/taiga/hooks/gitlab/migrations/0002_auto_20150703_1102.py b/taiga/hooks/gitlab/migrations/0002_auto_20150703_1102.py new file mode 100644 index 00000000..4613c4ac --- /dev/null +++ b/taiga/hooks/gitlab/migrations/0002_auto_20150703_1102.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.core.files import File + +def update_gitlab_system_user_photo_to_v2(apps, schema_editor): + # We get the model from the versioned app registry; + # if we directly import it, it'll be the wrong version + User = apps.get_model("users", "User") + db_alias = schema_editor.connection.alias + + try: + user = User.objects.using(db_alias).get(username__startswith="gitlab-", + is_active=False, + is_system=True) + f = open("taiga/hooks/gitlab/migrations/logo-v2.png", "rb") + user.photo.save("logo.png", File(f)) + user.save() + except User.DoesNotExist: + pass + +def update_gitlab_system_user_photo_to_v1(apps, schema_editor): + # We get the model from the versioned app registry; + # if we directly import it, it'll be the wrong version + User = apps.get_model("users", "User") + db_alias = schema_editor.connection.alias + + try: + user = User.objects.using(db_alias).get(username__startswith="gitlab-", + is_active=False, + is_system=True) + f = open("taiga/hooks/gitlab/migrations/logo.png", "rb") + user.photo.save("logo.png", File(f)) + user.save() + except User.DoesNotExist: + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('gitlab', '0001_initial'), + ('users', '0011_user_theme'), + ] + + operations = [ + migrations.RunPython(update_gitlab_system_user_photo_to_v2, + update_gitlab_system_user_photo_to_v1), + ] diff --git a/taiga/hooks/gitlab/migrations/logo-v2.png b/taiga/hooks/gitlab/migrations/logo-v2.png new file mode 100644 index 0000000000000000000000000000000000000000..01063fc389f3ff1904c8f9614753e34f27d344b9 GIT binary patch literal 37963 zcmX_`cRbba|NpOZbSO$Hk(E;+vdYX6PWIk=MA^HXgY2V{k=3z62RR2JBYT8~Y==Y0 zj>xgIH^1BI{r&v@IRBh;z0NgW*X!}TUysKtQde8$94#v?0N|XOs-ivsRB!;GQ>Uo` z0D0w~y8!^FQ1WVqr%#_g9eCO<0swI4v8ovg0I_Du2U6Occpm`3f>ts`8+baPeII(+ z17BZXA!iR)l|>LuEfaA z%_mZ=s-LLOrWC2ZlFg;hsI>k0?{gZer?)YghmX$wVuoyAdC7V9+lEbbXsVZy_YM&M zs~~qanx&#%r68xiU|`(HC11ijQUm+LSl`IxvXXx4lQ>S^{ATWU%j?r)vj={O*qvil z<-ZfClevthGp6sVb%vz(L-OYmcguM~`et`DB)Iag&tFQI@a^B}Uaq$?VGF5Ssh2x& zM>_!Um_HL&^q}-SeZcpf_GLA-^Qx>^!-UO9jn&SrA(0c05%PY&;K^QiQ><3>y>c>W z%dEYz^$vaQDCN(7o=B0!v~az^aFegaF6Y(V^30~uJ`4ahYQaw)FQ%pW9{OUBMAg_* zb~}q}Hct%uH$y({2nTPM8~;(bqld9EBd%udGmD&<)EyhzaTlv?Hy!)5Jt#UksFrhe zkN}_*Jx8y3Xo5XvPAIaTy7q_rU?`>~@8l@?ByihBwEW#qUSuhkqk~HQt)z?-h4qk; z=-;WE4a!D&$|t{dCR|(A0QlVZ35B+BY^@w=lIRZ^r!?^0C{C$9-qRt9|9;9VOUp1e zAgxmE?en1UNF_DHedYD?IE-7z{pe(3KdZJC20-;jOP8km(XYIaTk}q+xFzOWNec!$ z$9dI%(gG|Q^)7ON;XhdKVB-V-jaTQ@){Bf>55vzpE(UeWNdQnYy#Gb#L`S?~H=b*B z%t`EcQnb9DIz7aV@f_^*3i&WJKSXFgvtsE~acW`GW{Ah|8U$dtoL4^o2;rO0to+Nk zr9L}yGWhj39`5wzYxac9?S^+QF_~?ZRp0Z}4R#!rPY&}YUDx~o;LWMMv31Q_HAKK! zp|EE*SK<8Ou+H&^fw}v^AED2bjEdHTM|A^L^|5b`3MJms0F3_n-e!{#Thw_qk)4x# zj{n+?)}h^U9j7|-m`4mTmL{awgOh%p376450C+?HZ7eypo_WVzZCTAG~Z_Tq&Z;WaX@Q?KS; z;rf;;GL3V_=U^bK7g{{;6|lG5zZF}R$y|$sJhQOmd^dLh)oxfjELo_hBGLimH7kTf zzwP=QR9oim-^gv6x6D$MhFnR7!fLdBE)UhMBku|Wp!B20YwtZ_A_A3X+5ho0EyLK? z#O9;tQ^6f9u>c?qY6O}Nn_`~bC-6bXcppHaGU`1=;<7} zO1t*K>FC>DAJ->DL+4bK^)SDvP59?uVe77ozWHcJ2R^%a2O7(8Im1O1Sci>5yRyNT zbDCF=&xh6HVu=7CC&H=>PbT<}Z|$VlT;Mz%IN$d%^=b-74Ds6DAl<>jd8Ir6;3X4p zQuEb}^zj$gSwkdSA>;>DK1+V>MgJd$}T9zsB={^F43#7$>jL^xNf{UdH_ z7J0}sVZXHe5O`T43}7tVe`qyDo=K^Jz)o`o$?|=>=cV7$olCs>HY1uI0+18Ch3fa% zmQAc{JXd;mHPNT-Xke$g)GGHz?yI?GqXLbv657g+0Kh$k0)3}h)Y$N-z$Eco!)CAV z-=$?x#bVEDHr1uFxSIkXU^3D+vDGgau)`9e0D9F7GlOp^}cSzK#*U{mOJHsGsWtWPjc=CL1Z+uF@}zIA}N1t9*CunJAh^ZnFs zwwFlP7~-IWnLiIQ1I_znNKz}G|BRRa?4$+A+_&R$dl54hc+{9$j$TeBD-x*g8L|O(BAta|0K5>@Sud^od-{^leTe=e=JVRJ z@1nIkYSge@qZ*`Z0JIfsgx|~?zjt%hgy8pdH21>CCD%HKqpemx427M7wyy zCNHw13q=av3LDlaw@n=@JOwaL9?5y>`!|KIW)KxD0tg z^_5hB!F{E7#z##$8zt~O$GPK0=S4i=%jeQrHp%S#)+_|Th@jSyA^PR)dZMd;svagW zF{jqNAc?CMy*t(~J*Yef09-@Bdf8+sHRIt|dp|u)VsCAb!PcQ>N6me~dAEC<98iGy za933N&aI&vBq?L~N0{_g4(J_Tq&lx!-=PfO-rKoS@3R17oNtKM$12U4o{?Y`5~hv6 z=r#K|e|sk*;N&lHg3JJbUFhsVT4UUqlvzcHz6h#(%N^^ZJZBoV;z~zHxz*~cU2Z{m z4IRpN3-Z`cS2&={DiD1U=gQahYAlkrlpC%u?)+AtxH{{#2bD1v9O5(=$n?9`NEYr| zo*3hxj8s)>H1KbgC1Uf{bBiIxIb?tyCb4a4mlJeggq zclcc_bGAZO120mY@LEEfFE2sFd2-7Nb!_zY#u9Au-!^m+pK zhf$`sjU%QVb$a_O(0^>vk4BmODeyq73{80-rRbC!O z4uJuid`*~3)Bdx?pQ*#7V)7NI^R)37Ys91_9<4iOqbfePeeVcRIt+q<#}=H#!2>Sg zX|A?LuK8Qjt8~@(A^Ol5f|oojHStM#MTAZ$0H)0Y>igLQ`C`UWzK4Fgm_!V7z}#Px zLnGN%fbqJ0c|Yy=;w7QZP`2u4^|E~5qV5U0{N5XCc_lVZ2QdGHztvs^kE-akIcbd< zCNnt6(8fR0C>V+@v`G;0a8PDr0$IDPCPH#I^Lr2MA8QHI(8fRG-_>9C&NXNOH%Cgj_?Yuti(GZ#+Sx zPggL(R70(b2*Rohu|yL6;Csg7diVdAe7_w==jfwX{|R6D|MaPX)beUFzCJ5=q)_VwP@VoQcHBI>?tpj)^26gAi%IsjYaaKm{#YQC>?04 zEvL!5n%pcMgT*S#Uo*bV2#4ttW1>8I%*tS3#`LTBk%mJ{rqUN(3}f?_8q4&VFU*=f zZ)HTaIyP}}NXzoW>C0zes`uZZMGy+q%k^T?5*H~-G5gEm zz?es-{YB?=7U09sg)h`M6QW1+g}B;ZQIy-Xpmum)LQNWvvWw55qjMA2WnhHE;w4NA ztj;Eg0^o-=W?MRJI6as4;0hyL$!00I@k$E)(e$rDagr1OrnD%JvQ?Ya)ZzUWGJ~lM zZT#E%8qYw>K09>m*~KN@Rys;2HR(N*Pu@flUq4jScfUPV26&NP#jm+S7;x7X-l--SP=RJm*B#pUw=P`bJoW;Y(l0_m z+56Xq+NuT0b8VL-M@rYDp<-83fCITUiFJS=~WJTxmTg%UXJzuE`uVP<;kX>ui1 zAL+NOpW6-@dlN$s0iT(PT`R1KZXxJc_Jvd(Iq8cWP<{mnR>8>iZK7c$@wG||RuI4y zunU#Hv8Fg{h)!+p++~<5y_x#KfO_pAp@-P#NC(n3gd+lL!pcpl5mmaGrcfaXWp_M*FIcr5>%mwYI-PAN>P2nHM;NMr;A zO}CR70A@XHw4rv?N(GN%{A@FMldouAN&$ks!7<^jsoCKa^}^W{U{uGCdejzP#p<2< z)Rrm72$z};{F2tDgOF~g0?!C;vOg7lzUq|A+|SU(%rxiNippT=Rpxl(NgD?|S0m{m zfH(91s}gkh@zl^I&6=(YwDE7>G%)tQIE^{IrIL; z3nDNC zpxE2VoeESWe0=wKu?ClYj-QhkDY6oinwq8|tUSjn(7*W)iF?_)Pt^jUy>QfnvlaeBhvc5Yj10I%?i$cq$N z8f)EhOid(R{X@YP^SQ5$`K{dJoH+bhESG%_-)~-|h`^mj5~ekuy?tXTJSac;4zOA_VaABxox;^1j9~@5IEZgnypqMT#sO`Yz~1s+P`p{84XwRyp+ETIdha*(ZaH~~*`DqJ~{cojp zGqGYUID9l#OzHv-Qmq2P-f!6~yxlT$79cN?^f`EE(=v=0;Vd&1*598jr%UT|M#Vw^ zFKx@}%R7@n$Gy9``?~jkDgyy+l5=TSi8BEwQ-6tD zB&o+YXyeb;KDK$J*=LBRe_y^~z7zOIWf$JqXO|dEq|V~JfeB?I8~4l z&hljY?=DkJ%5n@b)B^1jY%I;n0UgrA*dbjm^Vr)lQgk%= zoBd5LBRuMt@mcCN${*bxDG}+?`4m()aRCPf@#iZH9aHKb*nG8@(8ZiO2-I&eAW4I=Sj$iHX(Z(a0={3)4q{R?J>sf1c144Ar zrvcLEAKNJg=(ME%u&!J=B1v_`Lb-HZ+BF{OD+>|MWw<~9lW)#KkH$hQF|>Z;g{!|9 zZ9I}#537CXa1*E34lqil9pW-753(x}Ofc2>xb5hj^jF-U4{LU&zqH?^REx5?t|h0G z^q(_AaxZ&Kp3vO?-%UZ`F=SQl;pnFHEnXT&I^! zq-K1gBm}_c?w^6T!!3mmoU86f$U`pvqMX6+@ZE@0?pe+Zyo)#m;L?;6c0-sIXOi{C06rQu$`{{I-9;OO@^GLKV{Zme# zyF;5}UXg2tuD zNXMzNMi4`jGz9D~p8{DMO&MuP&Q@sp50+`A&n|~T3FFwr^ZvwImXL4&%$}zYeG-if zkAmcBiYx6(g^p1TB5;z*4#vMK&gosOK{Ff$99E?vY3+;oPAS_eG6L zrQdxCbLV3`qlggTBz&pAB&zjl-D8_a0th8DT};K7y`dIgjcMh|rx$b1gmHt!wy>bB zaIPnKbL6?a$~r4vHusSN>6g1#Qw8o&5> zEUT`wkb23jt2&9QTc!YnFz0Vm&OFarib~r1{Lo zxY!~|s#gMs%N)pvqlW;eMhe7rZc#ecvY#p+StBzjDMR|M=bIJ?vKfB@S#MaAM);8_)F5Fqoervpdyi?1k-p zqZEPejg`>(bJ&b1VrW%izHR846hV1NA2f+eiaXgwgHPPJ5oDf0S7%#w76S|i<>rMJDk)#;D5rPYSI3tJ< zVA?=O!}F56TX_!ordX$*U**5H<=g`a*+&IGE>bf9yF-ON*8wippbw1I?f35FhY6kg=k^-bparNq(33z`$Ns7T^H*o9uFs0@s9XRzo zZK~Z?nM2lOgz3oHf3;80!-(lA&7Zl$NA<|z9+(*)DDHTocJ?wP{bCh>sb2bcNuqJa ze^|#L8j5^C3Jw!~gI=N-nq+n*c}O3Zql51`b$tgzu-*ulV@jAx)mUOr#P2cR(NkDFbW{n#?ZM+f1BUWi4oE32CtbJ}!PnpZgKa|sK%|^k?CElJG1^J>Qu4e(W zoR*|c749wNIiwGF7nTNN`IDkX6di@vYkr6qr-HTJsmYE`4Hq5{B}RYy1XN(*jq zfhP-?h55#MEG907k=ffm{h!tV@Tz`dbhQ!TPolX{Tqf6m`;nG^_oAQE&A{BU1uZxoO|6ZBE$IA zmCQgm0tSBZC?8x-ZY8Xh)8yU6qh>C{+Gs6z2bx7SJ3iBzij)4GA||CnvX0*n1W8iy za*?bb-WD;y+GuM`3%=9lXnuoY1n%ToKMWiD?&uiCi%f3kPB_pYNyQudsqf)rol{Z) zZ4p15o4$6usaQNbo~Oa1W-h?mM6B-JULz0=vQhBri~s%08|mAP{anff+wTI-z2*&2 zMtEmpUvQX_$enX^pbTyl783XIme5G4@iVC1-h)_rOdx8we3fIoN(ey%5;@MlD0L}r z73TOb$;olUlNXshHP#r((q64Rhg>mYDjs+mcN##g?%uw`lYbRQRqMEsvHsXUl^2=( z<>E+5FFP^!1z@=4EzN<0e7C81D98qD6M54g9Om$mYA$2qqXYs55|daYW1P}t9OE=6 zE^%Df;YB8s52xClB{L(4pM~9orRup9od(E|>?7AhQixGI8=W`1| z6&04}2^jELt~@R?G(F?#qJd6%NSR{t`WN|rjyO)Irk&UOEX*#lkm>43~sbUP5Pth+W#z9b2ks zdPd`(uX&kQW9cEl{_j6o-Bz4KJ{-Ujsfz|QkPt8Cq$tw@B@HRID-fiGVPZzSVI(nh zI{TcfGcR)GtJ1+`^G<^8KPz(%V6yQ?wC;b$!ppZB$qZ&H5HA|a;UPwi#i>D@s!8*_ zayYHZ!$; z`p-W7+j$eG%#=W%Dpn3Ch>A*=uOQK(>Lqf_qGwDF^NMeAL4iz$Q0QQ%GHGG#O~6v|7D zRh&c4pQR7XJ|bN!i-OKbU^@mOcJs|+zW_$KWNl+Wj%Y2o|3A$E$P1KUF^@2ZM`cwg3mH!Y z@E{bK^e|&HE`2uo)bcqk{RrUnGS+qA8T&`)9kS*N4V zg*VD1IjI^^=qo1*CxCt5a2Vpe16UyC!s ztw}Xrds27!s9549oW4OZ1Ex*e>%nRYBq@Ne|HH%xx1QqTWUzbA=57i~S>0qKx^Ckh zeVvWrMau5{&w!!DB)lfGe-^wo z-%dUa;&0Av>ntU^q0scd9P*vM?L%P^TnQVYNS<2ClxJ11_(ere2XE}CTVpIIaec8W zbAV~>=f8+Z0U%?0gTp=)h)%87^)bVl8>QEx2?5zC@T$~L$C~nZ`91cSY>uXcL*SKK zA9c|{HqaZ%`VJd*sCWqZj5U=%xLnp9?OJEJ4sGTZ6-{<|?HxemWSwN>fPymO>^&>x z+}h2*dq@4}mFFUi7oeepPe%WH7hr~Rgy9-z#!=P0Qp_mD3xlyQV_zaUcw$uMz)I}k zf<7~xum5!XrQ|X@I^c^f+k>xvtvM0!hFq`iGs$YBk?d~tMUU2z*+INfvX?>l*wgH# zOF|kJn16{Dp>OBcHOX6anFCjt&w=VEQ=gmPP&5j|UFNr=-}Kya1j_Yrk51}a?hEiZ zARlW$V!_Pv9^}5r5kz)f;<1N&(q{ztw~mk*K)lRsW2%CXOwe|lo%7B+etU?Q$E4wS zH|73x8fqZegcZX9MnH;^6H&15vj}&UO^$n!kUa#;#iyeluR%1B1vU3`vP~$OYE#=W z_R^>W+H9Y5M5nSMvqQ>QJEnAV*|Pt&?SW8tY>Z-fgl-+w?!R~oj{^KZww!%uozSO2 z+q2pu+nuycMKrxlSOJ~dmMJ>obPhjKUwGAvIV#j_2mGxi`0I0jO1b~UR4uP8c}2sO zs4wV6+&mmx=^iKD0r0d9B(>CB-xq84u{s`vN_|oRsD1i$Usg?&B!w|r*q8{V5G%Yd zXZYiV)NRE%V4PpBz>$;w*sK6b$%!J>Sa>laIz#}}(n@%4avR}Mpm5fCuMVYdgn9oW zwj)6&=R@(_ai68H@oo9&~%kR(DZW2 z46(-OYt;y7LG&qEkz$iIV`d84iyHjnzK^ugUvDS5CM@-h#h2?{pe4WPA@-e|%|dt8 zy2rvPQ0X1mk~g#jqU| zY=tG7)+W0_eyQg*@aBu@MCo0%Hy`F#g2(glo0AgOgKUKgg(Oo5WBk z|LDwFFgx%shu2?D!Fcg?=Dr9MM*h6R8a5Z)!Tl;00*YD`H5hTJ4}`wG=#*!MQ{t@| zhRNG%aw-`TzKbd=>LwR1{&%kA`?vVjJQ4(g>PfmliPr02^8}+lnWI zlG%}kwNY|T?|NYoskA4|r&Y24Me#*;+%tVg^?J3y0R5{6R%LA|B#Nsdq?gjkA0S6-wO!=@Vz3qdo~FB zG8+Y!v5bLD>0g=PeCg-219!Wqc)9p@yRSUvmou9=lM*I?{O2sNESvDA=l`QS1%MRY zD^!2G&7asq-V3@Erx@;(B+mqAqn4H78X6(9BkQ@Hf7_v{fm1HsB<`tlUkl|sGySoo zCV3`!MsL(JuSe@_m~1t+!}^zH>jtDV<~fgHZFBW1WrnZxz2>Jj4)Qpoi4f5J>%RmZ z8NZC8x#^=A?iBX*GJLL(vrC;dk_b`!_-`tY*FHBVU{_udA&P2rbthU=v<$Wv8Pth; z^nf=adm<2`Vefn=ua2nzteGUWZRw-w;fafD8Z?VLDy;y+^Hd4}7&^p){`~!^h>mvP zH-w1nEa%H$S0ag!Q{{fX{<)Buk>R6+IMqgR1Pz0rGglaYGo2CUO;oKWOn`Y32K-V= z-`$3*iIjg0Vx~2~)YuXA1^l-#P} z+Mftr#M(_f<)lRl;pFZ~E&mBy44^UY_+;vC8pen0dy!-Qi@HJ>wDDQ|niT$3X!+gc z+bvDVql+4qpJN<^yJJ7zq{ob{LAO6q6+Y}sU_P(I?4a{jo)JzlP1GLkIQ~)|2qX9l zAWi{+*0Fc(%UIgl1JP32WFY~@+{!uznCghCpFVW#CFvy$?%4N3N68VCy;tRb9>$Bb zTRJ>D7ON57Bhz5(j;05u1=nNQVQF^%7Z-KMA3$fuLiZC1?PPYO@&K0DvO&5AZ20~^ z|H1KC!pPmL9N8>h)|fX=P0*F*;Y0?cw&5)VUwbwRnAyWpUnU(u6~`p z!!$Nl%fx~ED4fjB|GE=O2(V~vCwk`(>uhPFnczBqO4Ln)Z38`low8$7NM49l#{uVO zkzGX4WEHuo_avzY%9SE$y5j$xy24~G_7-@q&{Esg++Kw1oj?6v=$vj{B*CwO%VQp^7^beDo{t$e>ffv z^puyqy+>Bqj?))kgz$%45<+c#-zkZqS4TYVJR$e~9(hz!DR&`WrK+*q#8V{?=z2ID zI~OTPXQP0hjq8>|(S}U*p7nFOA=!YcDxTe^?YBh zBQ69L#%s~6P?CGrhm$JH>VpF3zE6|NE6oz|Sx-)!ZtuzE zxlh@3Itw)J5vOL%PwF1vV?b>4%D;Zf<90Vz~72BH+#v*q{r0cBD3+^$$(XQ(>zmlKtrH2V@?EFodJ!Gvn zi5tNL_hqXhkGWZkycg$4cR=E+RHgzB*~>VpwhxL;<$NVhehREBR=X`#H!Uvb4}0lh z#0ofi)E2D&2rsTbIrsin?h$xJX44i{as;|UxTE9TR7$l;wewo;wGeT)IxgwfVKWw@ zWf=F{ADGtM*{7H0H^8_1VdTjxJKZD}kfNL4*_w8jKWoXp5iXwdfL`*CsORO(;1ucL$6|dB29Fq4iz+slAmwd?S z9#9kc+MH(tri$OGrr|EK8n{2W{~(53lAdsyH@R`DWskC&@5<$l4y0EdL1KdUJl1{a z2z#p#p{!tH_3wNhLhx5*jUL~8;9qO3MK*{b<(}=Wx=}wvq$DXJOfVRCEk16WCr9P%>|kV zdImh-QpJ3h*%DGGXGHs9ZC@R2%dvyvi%*lCx)zUpyAfA)8H2tB^B|}&j~2-r)!l&x zXnMGT#C-iif5+rOO-zBf$mGkUd1!OseENAIv+8Z9@~3a%{rg`E49d>c5)4I3iD`z zyq3xHXGdiYh+H3_Z@??=R4$$RBV4`kvpaqms_1h6V8DcXWJVgDp+y_I=uchoZ(<8< zeFr2KOxMqqlEU0pb3a;7BI9s!yB%a&NK`|+YX6>b|F;n$PZ)-a+i3Z{@z#AbJ^0>F zN-5iisIFf-u_&_o?(ke==l#jr&q0Z|j!GhCn68udpSJqx$u2ZUW<^8Rm9v(}D(-QG zm`s&$pes=6aT3m@=ivDQQ#JoXY19L6KI`yYBPrLVyZ(n(d^?oD-Cwfu7!l(#;=kp= z)^o;Af7N_3h6u7u_rtxfA*dLHNJi;{o-?d2Lwqvd`b$O3F{YiPB_4fwXFN@nQbq$G zzzwibtC|J-TnHM>GA0m zz0PplNYDegZ9#*pfwCc$QEHwF_)I%$ zG;=t{d@8VN9B*R7QAOzH4->f_-eSijEwZYPN_a4~C0;l}x*iUbuULDHu1*#>E*!i` zg*G?cY5bmJMUralpmr~j7_nBK10y{Fbu@>ocmvRu=Q1Fxd8hs-LHj%tNJ|{hZw^pQqj1+nP>?fZE>-mmcNBJV?I7o$3Hh;ll z6hWM%=aEz-@uBPR_b--%=X=_8t$ELT z<i8b6((;q;i z>9cT$!41V3@!ixZ1?8`KdQ`%9<#}l1fAE*^S~Pf7mukzrqlwm{)w|W;x@Cr@277(d ziH(Ak@W6?g?QgXkJ|mj=#3UK#f!Ab&r@A-Q4f{|2e!j1VKAUCbf@}O1ef`wuzOCZ5 z70NJwY`%^gjUdF5d3{svtxoZRrD}m5fFHoE_73oKeK5R1l zYVa^oVRdBl-_pKwENaTJymyA$xTkel+IT}H8zu0{5x>a$3AAe+446Fqo+tii>HIVO z_ul#cp-WkbZ_!Y;C~*I%Jy{KzWaF^iTdE0hui!^0IYFkaGp{5k;&9hfY(HmgX+6qA zGiIc|Li!-;GK30|W8-a)^!&?x}H=kAkKw^9mMW4>Apn zjGmxg$}f`F&ZsR#)7$6;DYzwtU3`C#uD)r5jviS1eau|`jt)G8MTEF6vZ_sKAPbY7 z484yMwP^L+sv7aFA?!FPQo<~oKZ)LP|8kW3tA<>mQ4S9LnU~B&>$I4&48|BS*+Pzl zA1vO-xeGsjv)U)fGg?d{PVOS8Fm4MEetW8SG#^8e{6ATb8V3CyRK?Wr7dHD``(9}? z+jO*`ym;u$HaMx+3e0l~TR9U==cyhSHclm~`SA)Y_J+&;S>(R#*pWWkd0bTEep{ZF z;pCf^4A%b9jYOV;-RJ&D4u4_9>E4UE-5fIe&gipX_)+ejOZX5T1q2_a>EvxSc1b9z zIG3HH+Zf$Z#N8tRXY@`J+)t;u*21mXhkPluScwK={?7C65u7sK`3Jw{65VbiXnHSR z+LkFSc(W?$HzxKWi|NC&O6#C76T=}wBPWi^DXjPbXABX*E&N~VU#)xhMuHr$uoJjq zQhn;raa2Ip10Hu_-|o@aNP7fhnldmBZxm|b#$Do z`Hdun$yMK!&ct$U!EQQBmA{eruw88fi#u|`3#A)o8f~ee8M0FS{rdy(qM+?%z2Z~gMO0KNcV@n?G-{56WsXMyos-vTR@Par(!Ee%R#|s zbE$Z1kG+oz$!C$}rYS9nFx%vi}mdgT!DL$QzvLd%0wGq=Dw1wYiaWwymOiLxa1iJ3q2ZeI$76a)y)T zrHV)IuGCR~O4Nx0szD0-_Gs!Xz7>-1fNKEWw%K(x$lrpNA;c(>aB2Cj$)Bxx7Wctw zye%k9GW_%)e?N!J4n|7;qZi{&|1H$G<9U_2HfYM#bAzxmy~~j z-)JPAHe0%{{kQ)&0OAJR>6Z_5HYgP953a*h`h4K~H#~UKLP#hH>m3_QUWbuB*v0m;wGB3+c;z|8la!8MbImYr}e^)zU zM52$sx{KO9+^Vu!HYOJO0a@hhzVCxR4T_8korxD?l;(i3oqmY>Wi92_wooqjr=85_ z&CM}fZn8CY9;_SAC-r3Z&+41>1vd~>n85{(HDA0a;P=XGEPAju(R$I3?LnrKJ6-a; zp+=mwnAHfr``90S)}|JlCYNZqIZ9>+=6L~Hh>JLgsnF^`4?6B@4Dq#S&wkT*RdLcy zC$oRGK-9Uws8#rbR@ErY)(+_o#(rVRY%$Oy^jX2+p^QdN?hQwmZT@uqIKm2+$%lpckC^aS=mFY`E?~^di z9VMFf9Hu5^QXLz$xh2BUw#Ru@C{H9&;VMC~zvCczC4f zHumxp`3G#YfMy`0VI`{Aeq6K2402)FMC*}1vVVaIfBrRHCHaH|-44)^b&=?^f3p~dT3-KaJye-fmNq=x9 zu>diZRo(q6ih=U0cx7mDfz(gB6O||4Uz*kgeN9{p~=>`fv`U7T$vW}%mc zbRQ!<&*rmD1@S8OZ993OuY#tx(aU|QH7b?55*e%iSZme8WrbE%{=jH)*njlhdHb|D zZKyyle)J}ncs2@{);7LN^p%TuZX9p=EuI-k)PuGjT5RRYMmw4R$1AQ?X06|*KV@?? zjyF;Qm*?KNBR+Riq_$ahr&`iBzgERHxrmBwITGTNFx`f{L|NnoU8WJPu| z|LW0o1odNSE1CH>TyaE*&EEdY-G%GvAF?|OT<_PTy{JPMU`X~ubMIJvZIdp#&nVnng)8n`OvSO{Ij1eXA${cKkO%snm&$Cs3#zQrf4tklKL^$Wa$g-Uw!JEboV>PsPLje1 zr!obG3$<6=LC_K$yk&|SVTF-LHm2PFk!^NQHXc}$$yzFu$)z<;ZK|WGe^LM5wklkB zN4`=+?9#3+2|ipapk+9jtoBy-53an{d+))TGiSfG5+$JIVlDDMkW-ukk4<9C^K!El zfc0PR{YL7y-PGmw8;6f z#2|Ny%#Pv8w{UbbGuS-zp3&nRW8f^x``4ah+giyH zxD^Gz_a| zouF^Wo#eB;l9@lO{gtHgD4W!-5fjXAq56!!U(QWM_LBF-d?&$Z`a~0by$1aO8j=(^ ze4r91$AE)ySLscb%YGcTp|7pzJXv+;PcyDsg4#`eiM+{ur(dqb-@T+P8P1zYDBw^_%0R*C z5C?~iij5?sS4u+FY%#t+!}J0~rJ2Pp@g$wrvK?U*$cZg;a4wfOtF0sS04#<|L9 z#zgHL-!`*Qs%HarXr4Ph>B^X*<21yU9r~&1mmq|4IPV7Wx z5rfAs;UKe*tm^DWy_*Q?XeP0Nt0#YE%8q5sydYl7aXdb6#2ODZ+iIGtb|okl-blfq z=$qrah*P@8m1%!IOk4JoWOH2$7KgAe@%XHHtl@m5{n0b-lPuArg3>PPpzvbTN14hh z_x(1Dx6IMhSs(mW`h4-CS(bt7a(|<8vr$>%$)hhTH=)gFm%S(P%>Nm8Bq`miNoiNr zZ|%FI>9eBiXaczqRO)sKjjj#T4RMdsw?gXO$Mr*=s=*>cf*y5QZ6!I%3!z=(RxSzW z;H?n#L%SBFYnZb9!9$tfYGNbJ2b(DOs&G6iYxY)5Y{$o(g5n>9PH zCITyzCgW99fO?*%s$7a4NlF)LbTq>AL(oqL%~%mC+;Gk{QqZ*JfOD4Q{F~L#h*O2; z{+bbDQx{A2Lepny)=~FYl2~+U^L<$C5dHsq7l6#c{iwWi{q9W2CtJ0X#<5DUR-zSM z_co_oM6IITmwYdsF?;J%-YW@#EcaVRC%pN+`Y{s!*?8HuSw$}APL{%Q4u4tE#aE2n zrMytt>f&!YI!s_F8j^e~r057bvVHF!MgDva5&WZYTy5Uz@I0rYS>i)|38pvL=)v>j z4}*&(*|u3f3&dsYD4>E7A+uCaHf4>rRmHNzE!y!if8t5^)06RFax7&nmma9;hzVX? zZxJhSUc9_9O+rv8Wpfp83zSj}H`Vmla3m>Rn}3vZV@Max_+sAW$L83-%T>+SDDPq} z?TbyFli*Y2--;zu?l{>$ypL0(KzR=NWREcMH;M<3f{%=kmacq)PM=vei8pZzK;&>z zejY#sb!dK~gI4ISny*F^G1Kb@o9>GP{UoV)NG|iCMSA_}=8acO@Z*~n5~@U+Ggz+0 zwLEmwgE(U7SJs7v9Z9?bP_n;#B$V%>f~IFoC?bve#9fUehIWx%E$3wd^Egc_fEwGt zrD@ppzyamuD#(+Bh-5OGb_g>L!doy=d3;07iX^3r6hBCcb4Fg|ciU~~z>StNSwkM~ zU-mHCI5X)a9>lbt?>Afb6e6v@9ypZn<`)L1oWWHRaFFx+Dr@n^EYS75uz(*m^&!?APDH>W1~jhN9z<9nf#8J`rDf!?57w-`xFtJlb`O zU@#~Y9ttkh$0VE@6t$!6zc=4scYQ~-^p)l^`Rn5Qr_1KZ({A>?>@63f4Bz*AzkVD4 zB(P0sm_o~6a6h?NCtOoEd%a~>(^pXvQmD@CXy#}wSd*9%Gsg zZ9_G~T7XUUXdWw6!aSa-A^jV{Hdp#4h0*#iBK&9lS7_E|Y4_uYT|s?M_tlnW1?FF# zcxW=&RJ@%8eiDxN2G2aQK*VyI3GxE}@ zqsu0Qv&JLbeAoA<4-+y}5E%e!Xc-ZSs}3;|=YRaNcx z`|RZyXD!EO2@iPsTg}GcF6mtEN{i`|xquL1P2dw_1#%4t1vNx4m~baz3`nlQ3{}nx z!GgOdGITT)oiof!toJT2>dxoMHaN4i4}vn5#cRI!=4%|7dJpmusfsH<%Mz{B?P^p+ zsUo?iU|SRc`ekn>CwTkLMg^F7x{KM5LY2Pp4FqucoS4VEyo3YIX0I;9Henf9fHBqJ zp$^DsgK?2J^8mt9?TOn$CYd&`a~9S9i)Yqmk<=J>)0B_Blth*!`N7^|OJO|2Lv0!B zpCN>nltRJ`EZiy5(G91;By4l@;5ccq?!wTTD$Q5?18rgZsu_#;_BOyu^jGX8`s%`_ zBWa`~@>}wv$~7vYYnc5cS)yE>nNSYcEOPtRoQPh1|LTEw^$Sni)OLJ%XijEm$ju2% zv}pBVK3Sb!#n)k1Ue+Q;2YG!tXxk@1@#UWX9hPpl4DE(yfL8io8=OifThV6Kt9~96 zD;h(t_6~<-VtF8CT*7;w8Zl%#CZTfag%iJVNe2Q59}V&%3vsoHuA+5a`}oy3IJJ&- zk>f9W1&EH3aw=2wk{?ck30t>_cd3F?4J3!mnQMXo+TrI(+daMf$8N6-X?@D2$277@ zGr6G>>xN}ud64WOBYoK3#h~4^saU|(7cAv3a-Q{oaln=X;lh;7GChilb-EG%3$21G z7R;Ct!ZNTJO_!@omi@9rnBJ}`cD_V=Q34dN7-HrD_!7J70;w(&<^5-HDxGu|s0t`TyPpw0%DPcpC>7MnqWnzw54ZqfItV4QzPQFBvtF|HLKG74{&N7lX4~R zCKxm+DYAp70ai%Oe9r}v0F6YTwheVY&;nAbm)y9u5+}uC6{&i|iI61L!2u+3wH^*K zV=1M>Ok$$>EA^Z#*~A0R=@LEf2DM)AC+peCnuTl^J4YNO!9hAH8GDnBa0;_8zilnK zdR>X&q}RCz{5hdra4Mm=Lhh#vA@6`LDR4TW1eSVm$Uz<)q+?hsgm*mFRxl+{gY6Vd zn<up zytFU#;Iu-?C@DNZlT~_Jz%~r;_*3>i?1SnTA8rj6D<_UWP#aI21{0qty(o(vKkQ!|v%5hke(*lFizWY||QUC|)3^9LwF1y=2URat=4uVJvh@l6} zH`6t9m14-O1sxwRj>lG=Y_oWtSYw@8@$Q==lJ0roN|n)L?;V4@!eli#~}wXEbjSmDkebX zP!vdgPC1SmVA}Kd^@LFT@c`|PZ@xMUM|?^lJx_P>4Jf5$J>Vf)1tn!Mhv1?d!SZQa zkM`O5f0QE9)80^szt8{p6DC%aUfO8E7pKnmZME&hfC^A|tD@fYhDswH10?j6f_mi(OI_dn`ed2r2%|QaSWYb&$IrOio1)OC zp4r-=Pu2=KfoEB8P`q&jrl&0K)$O&lSHy$Ut4}_W-xi_V?_#7CB6}a+m+9<$F>ftj z)RA&@2{`xRkGG`y<@(|9ll<8$O|QUdOmyW`uw<`1fSm}YWt7yX3~9h(VhN8~&YmVm z25-$}l4;BDa>NoYy)txUDUUW_n-?IA!S!S(^HP(h|=%EEr96H()ZFj=Xlcea} zO@FD^_bA5^lx2}e-9v8FwHL_+9O@grT?s{p-SY2~y*ao>GcSi`KU<3N(fV^Nom!hQd$T!dy{m27g<|e}-76QlTx_|>pJ0IUn=0(Ek^QKNV4J$v zLU_DHFC``jc%3^!G$v9PKfcl%t}wP(N3G5Qa;M2!<5(GFitqoeeyq~?U_#D$r|sL* zAdkMG26Yc0at3ZBkfYSAdCZrL!}7V;O&00ZaH6&)7diYWC*5%xpSbceH1bv(HE88t zUMk&3cRuQR`UjH{;@ftPp*o!ajL!=__kK??b`9CnD@J5#%p4{%6RC`nzAQMSkzc zq4q}kk0&wFl}^$|U#K*4;h?Wmp3jH4mOOA8q1@Ng^!}P}PF0<+iFI{uFs5Ee19mCv zy{vq+e@g4^O|kkljYCzoVwQbz>XtX+$G6#uRMH#km7yiv>LbeW1P)`3&F;3c!_@LK z-q(jaG*#vv?>H3^fYS>0GN!rg8{&Z(rGZ1raej(115vuawV0Dista`L;1ujk6w)Xl zWVFSY?@Z$UbE~?HR`G7hG+yM53X3n)-&`Pi(AV|Ge4Iu* zm=#QPC03f!FQIq_6YEC^eqyXjZ;OCDNx|Q}v0i7yxVLZ%cS&g8OJ$A5lpUtN+g_HM z^mz(3Q>{wYaFn`Xhe&%7dgZC6{(%Jn{k9$k<$Qd_DbtFyl(|L817&mfnL- zM5Rsxb8hezK+12nR)y2?)$bd-fiu0H+pnX*Q#os0P(j364 za$rwS_F>HJGyKPayF?aWw4Y&5(;*Az?-TrMI8e@MGNJW;{U|1uklt54K2Q2~`00zB zPA~fmG75nEdM#q<_tA&a9!4_Dhd=)?kG+o7xz=w?J*yngp~rRHeg_d_db>0j(~pVf z@O$Z(C9&Hw4#nt4ZDOvxJ71l0#vUVTHUl1g?VrIh>KB@jx0Ug9t4%d?y&U`Uj#wj8 z88jYA@Vhvz-!D+o$tf79tsS`U)SU}mK&zDMSt0-28CnjHJ`XT%IB%O>1+~jXkO#{> zb^ER6z@}w-uB96FwAg2BI-=#OJ*g|{8Zrf4iw!wAWkL;SWppIhOV}1g0;6 z$CCN)!JpUeAe9-Q04SqX3Z`sCfimn`&)3IKz*8(?ja)}PLh*^KZP)g1@8>EcHCs9b zw7?3fnC@*u!J0P*mE)O>_e&E-cdA~v1Kq&wmpkj7g%D~ z#Lmu(zyeAks8pKt}sXX*S~-pF17Vq_hnS3iM?-o96OZPGum zepWf2S^uCFhClu@aNuk!^jht3eHI6q(<45 zTSNA~lN}?azFaAN|m!HWo2`qEE|92+pTNI%ih~N$s78-q&n;rNjap@^Gp; z)NZls(n9WFC0|`jkr-eu4eC*j+wS}MA6=G{bg$`=je==DweU@p7IMiiUo$a?D``*O zw{O7_O$R`xd}Ka|tJFLhy!Z zOf2*I)%jkIfVM*!V%kF*9Dv-RMMnGQuR=vnoYp~w)Y();oejeDiTmPMY6mmUw6@Dm zL;upD+%_kctqdT=S*lBIxe@g1-pt<@%R9HYi`2ZTO(bk>uI`Bee%ZI0l6NM2Kj7JI z8ZXRI`Fl}G^hToL)Kfo4ZOw4oVKyE(jh3=-_Kc`}5HtX%d8;DK+EMb1bMU=Q#gabZ z>2$D<*HCu5OdQ7=>yp$M{t$CI{M{}z7fvztQ;{b{iRY|FrScP3Uiuw)?`rJ!UVsDK zO7Fobfd3{EfV{o`qtv7aVLtv}T@ArNje?9I@;y&a^*hXJDyYs969x;BfLoYM1$DH&R8b zGNpfB&PI5`7GcUjV<$a!`bJ+^d9-t+9+m*X zZ>+0tuNuH>0O!wQwo8oWeMFpE#|sz4nn7MQoTxkEE47PcrD&B(dBND&MlL$CHmoFz z;UM2Bu+D}(D}hn>1a$C{TOFXDw62mrOt-|Y`EzZ^)$y6^atpXE1uk~j6Br}BtrrelfC+tBf( zzV)aeXw1pIXhRyY+x-J#*@JbiT7UMdF?i{_)h*TJYN0ckf85(T=atJ@`PGmP->!;x z2malL0NyTEK8o1Ir0cMBJW5L6;HoG7W!ty}+S$m;0uFpa|{TV1*GFl+9=jn37# zg*n$kl#KnTAb_;J-KcpZYaaYKq`F;VcYqw+8k9ET%ZTj3#8Lwe{WZ`RT*lWW3uAsa zE?{C^zq7$U@g|MHQn#r83Ln40?9VoKaKYb_)qlanyRLWceuOCv4-(COt*Y#v998tX z-EcS1o*3O>U~w>K^XIwq31a-Qmz>Cw^df{jd{#2^sUNrbjfTI&hSiD}jk#|f7<4wA zXNxBF4HfKG`J|Jh2n&b#WGid}A?>Vwz=YIFx|^t5U|vgc+8=1DV6VIcM(8IXHGuRU z=~^A(CG83;uF)BiEdA*Hd82^~g`E~oD$q# z{+^iIc+V>O&mHfR6s`S0uG;zhY8y4O_8j`uowadD^&2b4)ZG0>IMtZL2gg(!1=C=U z+*p77K9i$Qr--2klVao~df3xCCR&hzS3QObg0If!>)8wTq8>|kVWJXDRmJkj%4~H? zHNXltMiBT;IB+a2HbfeE1=4I^VOr|Auw!;%V$P-J!ad`i$e}KEWRTh2-Yg^dXg*{35eJQc$!h^l!YRO4(kWebM@OkrzJz{dy`-YP>TNjp_#iP+V8npI)(M_3I2??-% z^eoaAno9nsda@#MKNd+!$^q$Wt$%RlLta$=+t%-fh|~)c+Ho zdt&vc%%B*NEc6IK2Kb7sw!eNUYTCuCE@8E3v3}QhuUGSqQF5_g#&|2Kk5~; zfU*5A(~Z-G5{2Z{J}jTCs!={kU8W1*So}a6!bEURmO3buG43;!4;AUYNP)E)(_8^V z3sy6)o3vFwr2OKG(=-!M00F~zW9|}}b1LOZ3G&qI) zQ1ai;SpNJ+gC6dQBQ#b^kd@r-454asSPV>Mw!3RZXcfmhZw?gN1hT5Zc+?+5?Pbz zU&de426xnGdHXgR5$a)ur{bYvPhVfX3LY=!^&)GwsW7!1ikfrZJ9!NTLoJ{K2f*Fk z!)(e!V4xSZQ9Lv?N-;2KS~(te&tV7gWYZW@{m#-V+eTrM<>x8`e9|pngNg6-;rv~O z-+h_iYb=UmQmFHhF-B0{HE6FkU zGTz%Mm`2LM*V5oX+-q4P_)TmFT1E6+`=E`sRArj57LeBjl%Z{=ur1y@d5!rda(vxlQ?#XKFbH_fO5>*;DqZE6VD=poW^~SyINdCG=Yvgo@(XLATyzT z(e05Uay0$64Le-;#?n|m-*G&JqqL~rDn15I>*)N!=n%`mqGrVXxCayc;q{}>M{7h5 zBAE8pz|%IeXPv7D-L1xnKrmD;(8G&*LoHTCi=aF^M}|(zMyn9cxU8*}c~Wc9`7F> z`p@NKnOJzEIR0q6x5Gh>daQrysh{9$3Pb((Du=S$7E4YPk+ZLFxBaT`2>L->V%MB+ z`46OZw68Uq#xk(*8gXMLFwwPr13v6Mf)qwrF&5p!komiHC&?Zcll5Rl(yROfMcZH4&2sv2kYi_nn3COFVhuN!93D@~-Q&2J+A9c_L6sh&LS za!6UYRuMTnRhG~9u<-CXoG$)(tj@I_De4oe5tIam@0)0Xa z$3Fbms;VPEh=^HUP?~ba+9^#yTPM>JyJkV=$9x+>d$bJU=&**Q8>g(j2VwiTn8}x{ zetOxD+)@GUAV8uD>;NX@S$l>)wB~?*A9kYMU@w zk*6xVO{D>F`fMm8-`at3bfC9GOo|A`Glphf*AA0Sky0U_Qaes^yL91B7mOsIRzGy{ znEfq^ABSLa-Wdjon}@9^$K4Ui_20ZDk)t$`!Pu|U4F@^u++?E1LlPJ4Q$a zZ-b!>!cS$RmTzS7n$diq3Y3v211Nhy2BC@p+?oZV%|0$T^{GrEwQjGPTVI);6APnw zjf^OZ)b7a_$KOejt&x+;!ilE?Qz&Py%S@s%eUKM584^R7cj|P-X%qy`U<@d}qg6z6 zf@?HLE3xU>i4rD7q@w*{Sn6O8uPpm2z?C4Qq9tYXD@{_&h5OrTI1!BDFYsr0Z>wi| zOwx=}RA?0f|F9b)Y%_zA9Pn3s_k^H&j&aP>X3ghs~jse(i-$e)X4dR z_4w zwQD^vq?g4Qjh(a+-rq*nBjw#7cdGT?vyaSQpVRL)jzeYG5D}4>D=_eX-uUBZ07E)b9i=$1XHtF^KwJ7puX`nJi_%<~yB&ksy(9ia#vlk9SW{U@(-4udhQbFgbH&Z9Hwm41^=Bnn@T| z<((k)__nATYsfC|waBvFfTqH+?6WR0-Jv2<=o7<|8+H|wvx#tk(Mo)U6&lVm0kHAJ zX)=UDCRPEvt>gODjK4Y-Ckoa~thN@kk4Kl5)yXz$?9=rRE4{l%L1C1^F?lTb?)I_~ z6SS81+38FUwZT>Kx^^~q24n@=zB2mTgBtk<*>GL+%|+m@XAyCvaGfDpB^(rQQEL>gJrSec z!dn7y^v>602nuS|Tj(}}axeQ4Tj zinRpG&T@xeajyd2gAsU{HSBqJ-Iz%4ixMzBfA1`z#?PsHD&^N71V|)fm)e6*$kwcn zr@5P&%)7j~qigUI{NG#vFWgIQBgftcL@L?>zmR$c^(MI9w0=itqWoq=(D434w%zLi zZHFdUK1)t6b=S3#@iag;4gLv{r@BerHdU(AHUFjTn>?vTSmFLKA|^MBQ}RVD#*&RJ zu*RaYR%-9$wEESfgNWQ~tCBXiN=+oXs;=)Ake_1UbaOAa2fw6TQ53oTXGxv+N6?_~ z#DjJ?C_a{2`1`S?391l>5xT{dH#7G|TgiaYov-1sj;s#mTqG^f+xduT@@Q8GP6{%>@2hz*E!MkDf+Zy zCC}U2aG2QbZ}9n`fTL&P=TSmribz3ZWOwAWxYe_wMl!(DVu;?+2F!op5%GDcXsG2c zdxf;FP>Xu*wA>mM=HBeoQbr|!Es>>K>Hf<>T=UZ#hzLOtD2?La+2^NQ#>(&bKFVYs ztbdjc?deOv^4(2?^>PuZ8s}%l&ZCXpC_gc7jXhFnh zhh6$a0nm{<K7+wvfm<-2q2UR$RG1OqjF|oeB zn$f(N^~{wGSn9?aAl)UZ;qW1X--Z&ItFvCJGr#WH>6g!hc;mTK4}+@2KjJ~4J7mE_ z%o3E{)`=Ni(GxEihDluJFXJVv0eh8In zD=CZ;TwxC!G$t<#R^CQQHSeNY?sV2lJx0q^3L9&dZv2C2H(tT%PkCrVX6>~#GtMugC^ zfuYA@hkp<6)>($XR?%|hkBtcBYb&ic=3Th^sA~>hT zXnzpoNEA03liXgAfGnFir$#9#V$zRHn)z5 z`XG6&A*Jx_XyzWb)VLt^_N85W-QJ6gTxjdOIKWIf;h}tP4Cz~C< zvd7?gD6eBxW&L9)G0aLo-h&gb%+F+|Bxhi`EuE{r;XR4Z6#pLy_g*jtm~X%sbHu<< zS%IqQ(_E*s(X^!7Fr4O$;7|#p)G0%VWLbzm$r#-p{CQls9(c=1lJZHIJ>_S#iuZz3 zh*{T|cxJ(Sa1mh*peZBF3C{aTf+~l9@Z)LTLG{$JUudH3D*AOkNaE*bu07oTcrEN+ zZ@R=<(0;ud6gx0W(~*3b?;r@!F6wY zOD&GN*#vEbA4abiuV4gN{{`G>jmAY)>CWvL7irGdFO%Uk10WEj`i;1XmgNWXUHU{? zlPotLb9~p1oGHN+ymIRmkzTh~?S_}h&8k>M>Rv7)OHTR`dH5{O@j@99VOn!QcYQ8T z&EW!p;J+5L#*4Q1rv3N1flQtA8aCQ8V9IOSEnFCp19g^FvHq89!+uP|Qaf^*!Bx5)1k7Yd{oj>*UN#{mCvbSo+z zM}vZGz!-fgz)(xX*4~R$#jPeppqDTp;CIT-zmhlgHdYmc(Qcx)ib&Hw z#|8GoGD8)uYOHD~gV<8EP~%r+f$DSmf7M=7Mu5c%hOfGmA)v&tZR>XPhIOqKTC-c|#V zd&qkkrTfv~D)QhaINg(RrRdw+ET2>93W78E?vU|WTzoow+9vf3@H>LYdd8)fvPNy4Z*k8UyxZ^b#FLS;ZJd28L3W=yDLw3RqOGkT?ZBP+t z8G?l|;LN#e`in`87OM7>2$bde5<9bbxb2`&{ERhWnz4q{`>atC!@zZ?T<1voSv+3F z`!!TiQRvBb zjF!DkWaFZnn@1{~eyU}knl(P>PJ{b{`<)eJOI|ol0L$L6{m{1HLXAc7+K`i1=oqk)c!GKPicakn0-q^HnzHQEQ0si^&ro${ zrZ9|b`4@^IcH&!#5cdWtEsj0A1Z@qAgy1)B6z(~poD|$x z?jn(YfcE}gX4U5oRfEhLteE!909hRWu=xGAfn^vglFgDRG+iC5NSE+gAV3gjx!KT5 zEgDKuf#xh$Cy{JI)2@E~x@qLRxYaX{utgu@wcl|UNLht*f8XGAtVE#_@m0TB=ev}s zuQ%@VWG1Y;CrUXM-II|x;i+rP^b+5FHt!Dp}W&3fzMqT!r;UB)TD5}pj0-6#% z&`)@mXt+N4-xu%3eV?v>VI@L}?bdi8Nu<#6mg43LM(A!RdBE_Z4bA3U-hA*k0k5OM zz0Z${;In2+4LxNxs=rw7q85h&o!rP_Wq}86i?OaROw>iQ&Zh}Yu*boG9%q|Y!NgOW z7n>j50_R`)gBNNz2}iU$ioQ6A)k1ARvC-*tI8dzq9mf#q(hvGX7Uyax|3hgv^ZF&_ z68p4G4aTWsKMAgTEisvFV*Xc+3Ss77iD&RHw{G*R527%6>}8C5yKK4anXhSS!lf>H zQoF$Z@dn`qU*k2^bjpAyXSau2+(wmS?yP=q{z1}s)ZC!&?f?-e-lF4^BDj|~Y+$7R zc4V5`&5|$c0L?_`z(M}_2IVfjPaf;>7G==s##Yn6Na9KHB-x#p7^AFkp*6*UaOddtt7Um4|zb(WFf*1(?-rYsuLgQ7mI39p^vphdM4QYww z%x;g@C~|De-t_$tSt>qB5B1My4(Se*xqu)dOw)rHsfCu~E3$B#l}Ge|^WsFUtG_I7Cdw1j1alWHYEZFPvD|!21m8}fJFq5jV#EL0CYtV~#Xm3~ILO8w zZ}^IylDpuB#(DwE2`JmURU_s!I3e{^;(_F>{h=@pTX z)#>rHe?60J-AMYa;3vHp0g1yJeOm=V+akkXHVSdNB2GH*YFo!qHOU>S*x**slt>sw zGC}4zakDFWXoAJY2gj^cy@$W5^t=PDrHMZ;YK-8kEFM-CO#F6YiAajQLj*reA5c+7 zSpg)mlD9&+(k_R^GcP|X$BFu#Qp6Et+{66l@2hCdVnjT(SzRF{!AKJwT5Hs5uP6BO zJMqt2;2F@%l%$9+kf*GS~pl#b*+uix?3+ehSB=AuZrgQk5Mubg5}dT>miHtrgaBa z!>Z0p)PF_+I#nyIVABz0q;v?0%>~(dn0|UB=n+|9jmqe$Vl{4>W}o#vhmv?2Z&=w# zt4H6^E#kG}N<2VQmZ563!FuU#iU%=qNt--!Rc*514 ztnUN!a#-$$!0=YRpjVfRLOEn$sJ6_;f2@+>JDQesq-5bCLoBz|VZLGjE{)v@;pb+-Txqz^AUT(QlD=x~D|#kQD68Nsv_O2o6#adP^*JnLBY z7a}!V!cK+`CTsY(b+#MhQv^S^#sBi;*1 zJAUWkCG?+6V*Qe<%CGjW%$a>~Y!ygUQ2R-gzber*j*|LXmjr5CjIDgHh=k0gu?#Y{ z4IBc=f0MPzxO;L*d;~nwNQak7X3p^-SMaljMXLJC9lWHR;^0=et~<@e0^D5dB5*UD z$Wq+48g5#w+`fODq>=|vqpa`82&JbCdH$IDiDiFryxP(D^Zc$?CR}d`6+06WQ4L1+ zcLMn(ztrLgWO(~l?Md6{bA(f!p`}_Rxu&H^)r(APM;a^OX%*a15We+fp*FzIOV-?? zBGL~B(QCKDJriW$6^kqsKNb!=eJ#)QJg^mH_BRN?S1llxQm}833F^(~lUW^+wnecl zw}O3MSqc-V0xaIOYevLeWYG%8ka5KfjbX)@N&r!7rQ@X5z(4?>FQCpc{3AT50}x=} zs(r75e(jndUxYM8h&d&9!#L^+Q(njuqw&LDo^Z2HischI6}_U~)=u-y2VVp6XgIwY zGpP?qc)j%YJ*T!@6|HhT$K!`LoEmn$Uumk;1=p7Oy@SOUy5OcD3hO$uLd32rTZ{7q zPM0B0p34X#OR?Z8Kyy|-8}Y1?!WkI&1tW636bXNwpuMD>F$^;7C2R4dVzbT2##e(YqO?Ul&xfs_H$fXr- zLNUrnRnMvJ!fkF+rCC3O;RDlhYn(faI`QYe>~+MlGX+{f)%FcG_l7?0@Y)VSgh(d< z($*~F&S3er0Y;;8_km>AHY+jZaWI#}VUscp#}ss5?Zq7;>hZLE=L>Mx1V-S8_D%Eq zzredEP+CSXRGW`);NZ$~V7P$PF1?Zm?uze!Aa%*xe+k8iv=N3O224zO{E{9#5-b70 z+qTBNt{z$+Z@|Al-mm4x-qJp%A$7D)3Il12lwqGSLj7D41LnhcM`GIJyKFJxSOzmI z0===F!t4tdenMY@dL#(Vp-4Uza5iu#aHzLF{6#>Ug^x=erKxYQ*?43Y*h;RZIl6*) z9sduv?mx`y{!dkaKry*mi7J)}K9i8$uN9mnZoEQrNx}yjp4Nl;iXW~6G+E3K_13@t zHQzML#1jDlIv7^?WKGwBYSMl0`M+2OvsY3CFjM<5rbO!78s$dAxUxX$|2ielP_?N* zYO^871$Jo)yffPVqyAwV@6kaXTu;?KCsvC1Omqa{EfJuUe*3x&^j(dL<~DbH)AEh` zJQ;(hv#&*eC?X*`NRn%2k3Pqi<@2$jBQ(8uu)bFw*?P_9C=!nTo__dlE|N38B~3)- z9Fg!f(0g22DDt>&X_!tc%I8>z$jqO^0fuTb1$lU(0=tir&(i(S#vNyBxbUoXviz4lB5r5^$HRMn@1;@6RpK@N9Cp&+KjrhyPYM$! z6X($~zC*(Z@2|@Iw4G=x=;L>Ay|L0D)2vl1KrP%rpNs4m(Y6>XdPbVMyjLS0-|x-j z)1E_?*vSE4R${RGT1dQqt&n#k3>8yu{9O(LC$TWi^$F6QR8uLpWjTh?oWWTo_GmLfYo?dGi79m;$mQfJvAX8R*=C)_?= zPH^ExL_h|vfFQ-MU#{YK1HFcX*q7eXtu>V1Tq(9CKD+tQmvOlPR!KQl@;tl^hw011 z-@ql(y=z~L6@9CSgbYk(=JQ?uMSiVyw?Qx@DEqx@2Yk>^>5i3>R2vbmJZ<#)A@1hG z50QdThRE~}>%+~F30D5Ncs(zj>OxvQs&WZph;)U%XpLA~iH&1yk7veic zIBU-{;M7cVR?OXaCZUDQd_J3V%fV`9hL$zrTKgS{Q!r!TK=y#{$zqsb# zx)Pt_GL7M*KjiL}0@T6>Lw#g)Z-NkZ2-(TIqR-kFV^LD;08MRx)GqyjlwzyKekt6r zWQc32M)Hd%?v~@*)cgF3UbqL6^Vcbf&$gHu2TMVcfN88G!yzVln>`7u0TsCKB);UwYCudMdOFYLuqR4w*S&AYEt zEUBLG>;LG0{+53+%Ou=IQ)lq5|LNC4{E9=HmQzD|^WsmGH%DVj{vv)b=f$uH~96!~$}G*FLxbGK|n#L^&+ zM_ZP&lQJ3QX>?iYWj06mmMSn6ZfGQy;v!z%hE&3eId2f6yf!|HZME)JDlp|0EZTPg z3WqV97>W!Ghrrr{Ot`^|CxvN7QB~(RuzcDqPe^OgB+U3?H{|<-J#R-oUyKy{tCGNv zf2W@maqHD^a`4Sp2MCPzfdHYO>27h|`PVo(g%;b9_M{vi<876#?CYsj_;&!X-LKgs zj52aJFd%MXBUT8@@FMLY-N&x52D-XJ+x2F@K$1b-LsWPnZUY!XioUGU9L1rF}7-(QK0>$k%7y8Z_N)`}%}T~ikk+%WMj*N<+b>P1-O zep+dS@fDx0UV-pew;QTxMSY(hPdmb@wm^>>bZM`(?6$qt1TDA1Dl6_a!5DV-SJ!0E znEE|Vk@&kec2>q$V^Q1jKx(GBfy{h9>Rq!!SDXdVc1MGX$Ji#p!4Lyk^2M2&#Gl~N z@IE;j79fTJoMl%r+e4$Cw6vq4{qqx99BTXeeU7Iyh6U{AF9&PvJ;!?a>0M->8`5(3 zu2~9y@yD5o+*eC*BBCDJmD;CeDgO8qHdaIRu+#jv4-vwjqy~m+TOt})du&Nig}*zs za*zJ*Dp=y>7?yqB+&ddl!@143Sh`q9Xx=@Q@abD5q)&celhrBsadNsG<^ZzcrzJ^% z*_%!c=hn6BV6w1YjE9wSX#OKS1M&HUfdc3V&AH9D!k*U*JC(S*3UsUuAq&GwHU--7 zb#JnLP91vo($rbbNc-R;SsaSPKz{e}V7}U(A=3)`+K{<~1_ubORVxg(3iJ8Vv|HKG zGvV6?`}I$CB%uAPvd6a#>)ledejJasdo+-mNp6XB$vSIUC*TR3l2C1+x}WkQ{xYJtffyC+b787_6y(DVN_R6I2uk4qTqY!D8Ti_CGm zj)`A~5LAO`&O07pr&pfZ!Cwon>&8Oc79}&EO7L~+N2AhXfz(WI($%=s*0QdzJpVDC zb{o6OdOILL=uOjgpFIHJ%Imw@8kUNLBlEBcOo^4W62n*)V(jxZR@Y4U;B)G?$7!oD zMt)jKFjU)p_kRqQ@*Ps*_7{)*phOns&?V(!R`YNiO)ohS3_!+OOjk%DJK-a;`AC8; zDJ5~$zoNzsg#v(~+ARG4^;)z8z4UcErX+YYgB(O>xSDMhW;OpwQU$cN`2|8$pwweC zuf@zdx2j|FyKSPhd7|7tDnCOJ>=cm@Gw&y!UixzwFN1~9 z{6}kW;&VU4Nq5{$&iIs`^T(dU_(F+6O+YRD^DJSdJ$h!3G4)~^6Ne%squ>ChDa(7e zaet8%XM#w3v^`A!r<-3!hGM`OIsB*T*T0ID#eMY;K7IRXHOOb<$qH4x@vz?B@%WA0 zJ=}lnm7~Xw{7K|@$oPu&_Z6}(`{1`IhF0ubm(uj#f!8Fs^5oEgM?1a7eG>(@ZoGI> z-UkBg^u05?cE|D-hvEp1N1H{AOC2RDz0o^j)@pN+1z-dQf5LoEM#Wye){~pX@Y`z4 zF6!c*9D?%CJs=5k3!Lvgaai7I2>RhqT7fM*lLUWK>CsaRdL<;k;QCE(++jR;O^NH~ zAJ0c?re9ULEvI~sf6GX>id?&@7a?)So~}~AU=$?lb@1CFlzj4XJM&YNFSn+)>=ja+ zeHj|$HS|VBt6RpMXLnNAhCBYBnvCPGGHd^qz#grh+B{BU__<&7ABTlWhaCjrPeT9e zv8>|Q)3cnKKYHJN@Ihc-;trymmK(z46TfQGAU~4xqly|T>ktZ*>O&LCn zk|vIgG;yr%VG~h^i%!r-jEr_9`crWDIKD)tofQY$Z=d;A7O3yA6L9?9x8p zX@kt2^Dx{QUEOoJvYbyb803TiTy|ezyate8+U_p(Q>us}i|-8=4cS~B3{DTeUVH8R z5PQ9)5+Uunq271hvg;t;e68!77D?%2{dYSpvkdaaN+lApQt6QYuLCo2hqCYD_#j4> z%uNhNnA;W^JJ}n#Wyv;KveZ~YwkOF>_As(W5|JA9W{s@#P{tY_Q!L^n%@VFc&}l3AFt%Im>}-e(ka1%Tye)VHuG3ov?ez`WN+`d zO~^v8%by2yGGiE>9QRk#B|_gKVYc%oQ!66>e94=qx@#4FQH!+O2P5myu_AFc$@Wh} zS4~a$?{Uk0EPpwuEwmR!*%c?gOlDyRFj{*mhEJ4Wt%eS z9W*df=6EVCQRcr9zgrj1`|fB)!3TEPMFE_;EG=RUg6zD0w=YeDqkgba+dE>B7Cfov zTDvxbc8Yd7Hc%scpeA|?x7^|azNOM{Gcv+?^Zv}VN@UXt%y;4ZQ!D_kXZsX8 zfbp@M9YXVc2x0zZDz%3Wsk?l2-efE~$6_X>b=tUnDKW&yUp4a_lqL9 z+h40Re@sn_d6(SVbv9-{eP??Nc#QDPm;rYWEA}1=DYxIykTKwxF zD!0@{7w-X`g8C;SHXhb{ zis{Fd6g>X)VJ?y29V3Dz5SGyI`gDZC$TBmQu*ImVOSRWpzVXVZvtHN6m!cA1I}$wf zE}9;X3hXy(wqVjH>PZsW`5Wnn7AqTyhYOJq#Q7It3AplHLb`A*p6IVAP&sU=HsBnO z6sT%kSY^Y1wessA@qM9+9Xn;tnp8j9z-h7Sdo{rU+#I8X{MbmV%(3XrFzb($Y^Ujr zFX0l)b>-4}V!;*{++{_Qp6L$}1B>3XH9WsY)3Ef2((U)>0g0V~Ff=HaP#nl3xYNYB zg>w{sZO|;KeA=VmJL5HX-BTBJi+xx4`9$ZDRFM;n@uS7#>e#jq4CVY^We%PDl*7Mo zUTu0N-;1iM7BBy&)@>ouS_;!4E%U$U(lvwPH1ij4;I9m`DqMq}so#7-R+$eOFIczg zq!~dC;eTUvczCgIS-?tCCMcb3!R|2LsoGrMp?ZTaKO_n}mCqJ3pCcKZLQ7l0?E}32 zP4&;}n9ZdR$)NW@(235-oVEQU>B9NYHhBA-HOi=#1*KLiI(*t5Za0u+dqclgN3)Kr zxAvl;sQ$Mq=0R%?!4Lnj{W^9|8cj}--1|& z2vq(y`n|IK!I|{o$~(&wtbSpLCxw?A*m{M(F1DH}ND6)xf-YW zXGPw!T!c<1n9NcG%+;E?+hif$EZ>XdaO0H>qN{&+Idr8ki+%5L zXK*JlvQL5-+=cccV27|Nj+QpzU~kpThfc;^6B&Fl(k7As_Ta$mZR*41pys8RCgX$d zH`c3s0KhPg(DQoLO{NJ#0AybI8NwbyMlab~Fw#YlgZeYNt%nVuXD2U7V4EUr*|_h;PE zf8onbhsj4$V=WM*hGIPFsZt;`-(Mm=M#+BMU?!q*=|09)D0o7{48^;IFkL+$A~{bN zYo0nuPdb8+iyltRvN_}v2umGI_XuT__2_XILDd*cIsF$(I2oo3 zGivBB-}s|b4A!>M!XYU z=k%o1?SG3g`lA8~M}R~*$=Ne4YcJ{*%fknmdcFtsm5wPio?U*I=}~+WIT=t<$i`Kg z-DT;Y?%moAvp79|2b`2xipgWJ`to-u`GZCb$!cD|0|`erA4s`RBsh(c=ELGsT`L`= z$SDa9)d#Wh*Pczp^C7NZ^=MU<`_W3-6$dN+ZsLlfb*+%$^)tUChYQdN#-5c$$-jQ~ zTEe=LUSD-?cB^mO+&c(29Ikl^gYj}lAoa;N8brmO;Y(zRAO4Ad>wmK@RxT>*I>ucO z0PJ9l8nAxDd-Z9Q{U%X+cIr4S!g~i_T0+fcK<}@a{IVVu7v!Ztyv7YZn2kwvyy(p) zI56DV&2*389shtn6w<2LisUOmpXYS&ol~Apc>HuH@E?^nijFA{Ex1GrX^0F^b%7I~ix2 z_?5@?D7Rr^D^sw6-}-`~{qRJPW7pJC-JR zZG4VM5s;}U1_jRrTWxDoV$(N;-)2g3M9>yuGcePkky|dD(>wrRhdlmX&j;Y_P_ZEc zS6T1Cw(|8~Bez^K#?eeq6;nw>Qnpd ze2kfolCxAf{F-wE_kw zL1I$ID^~!(00+L8)8Kk#*g^UHsfRIl|JPg_cmOXcQSRNG>8#4Sn!3Eas$s<>DR$7_ zd1j@3 zZ&kUv(XArVL!V=TO>A$9boCw!I=5)uMF1EK!xlY4L~Oqclv&ukx*P=4FFyKLc$~QL zW0*eMn={6Yy;Vh-PBiA=CI6FnpWYuFmX%IX z5pNZM;gKF4aR|Enkj@?JHL@x8@u3h{xz%8)Yp*WbIMu3YQyg9$cG@PH=S`rI(;F@q z5&fg#^sJrRj24>s+d^)+1!y^PVv4Gvh|jPcfm(k^CP)Q$nXBhykaQM}d9L5#XfKo{ zx1!tALMJvno8SPz7();`sG}7skF!x5;p9C2k)$+4ItxtR|Mq2o zwzj&=ms`R@6B^KUQ`zL{IzkZ$-foPu8iK+0>({w;7QB?CR3Yw`1|w;)f(jqHXrzyB zlX=!fYaUn&bHL@|sn`Gya8hFRuH)`0u170Y;vkVVj)ffn^twNi;OYO_yMm9ceM6A; z{mw9i)yik%%IvDWOH5c^P0ZTx{nRcC1%qv4OYOrP`fgS{f#A)-*Z_3ak$gSROE#^W zp2=rNSM4R0xH^_hY%OP6DQVq}MqIaiT9?GjlBi^E%0g(#AuVs}0sz=4GNfvy{S*X` zOCnWU+qHvw$>PS5JPfj#QO4SbZ~Q_$^0%h)tA2~IP+>vJy$b`u3Ot1g4H6?Pypq$O z8(hF{Br$y&t;O^7YTLfK^3`u Date: Thu, 2 Jul 2015 13:32:19 +0200 Subject: [PATCH 053/190] Add is_profile_visible to timeline_entty.user object --- taiga/timeline/serializers.py | 3 ++- tests/integration/test_timeline.py | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/taiga/timeline/serializers.py b/taiga/timeline/serializers.py index 903dc00d..85a8c77f 100644 --- a/taiga/timeline/serializers.py +++ b/taiga/timeline/serializers.py @@ -43,7 +43,8 @@ class TimelineDataJsonField(serializers.WritableField): "photo": get_photo_or_gravatar_url(user), "big_photo": get_big_photo_or_gravatar_url(user), "username": user.username, - "date_joined": user.date_joined, + "is_profile_visible": user.is_active and not user.is_system, + "date_joined": user.date_joined } except User.DoesNotExist: pass diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 05c649b7..3d203494 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -22,6 +22,7 @@ from .. import factories from taiga.projects.history import services as history_services from taiga.timeline import service from taiga.timeline.models import Timeline +from taiga.timeline.serializers import TimelineSerializer pytestmark = pytest.mark.django_db @@ -391,3 +392,29 @@ def test_watchers_to_user_story_timeline(): user_timeline = service.get_profile_timeline(membership.user) assert user_timeline[0].event_type == "userstories.userstory.create" assert user_timeline[0].data["userstory"]["subject"] == "test us timeline" + +def test_user_data_for_system_users(): + user_story = factories.UserStoryFactory.create(subject="test us timeline") + history_services.take_snapshot(user_story, user=user_story.owner) + project_timeline = service.get_project_timeline(user_story.project) + serialized_obj = TimelineSerializer(project_timeline[0]) + serialized_obj.data["data"]["user"]["is_profile_visible"] = True + +def test_user_data_for_system_users(): + user_story = factories.UserStoryFactory.create(subject="test us timeline") + user_story.owner.is_system = True + user_story.owner.save() + history_services.take_snapshot(user_story, user=user_story.owner) + project_timeline = service.get_project_timeline(user_story.project) + serialized_obj = TimelineSerializer(project_timeline[0]) + serialized_obj.data["data"]["user"]["is_profile_visible"] = False + +def test_user_data_for_unactived_users(): + user_story = factories.UserStoryFactory.create(subject="test us timeline") + user_story.owner.cancel() + user_story.owner.save() + history_services.take_snapshot(user_story, user=user_story.owner) + project_timeline = service.get_project_timeline(user_story.project) + serialized_obj = TimelineSerializer(project_timeline[0]) + serialized_obj.data["data"]["user"]["is_profile_visible"] = False + serialized_obj.data["data"]["user"]["username"] = "deleted-user" From 81fbe96ea659a74aed284b6588bc86d7003b311c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sun, 5 Jul 2015 11:20:28 +0200 Subject: [PATCH 054/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 255 +++++++++++++------- taiga/locale/de/LC_MESSAGES/django.po | 254 +++++++++++++------- taiga/locale/en/LC_MESSAGES/django.po | 249 +++++++++++++------- taiga/locale/es/LC_MESSAGES/django.po | 255 +++++++++++++------- taiga/locale/fi/LC_MESSAGES/django.po | 255 +++++++++++++------- taiga/locale/fr/LC_MESSAGES/django.po | 257 ++++++++++++++------- taiga/locale/nl/LC_MESSAGES/django.po | 255 +++++++++++++------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 257 ++++++++++++++------- 8 files changed, 1342 insertions(+), 695 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 956c3a16..8297fd47 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-09 07:47+0000\n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" "language/ca/)\n" @@ -525,20 +525,20 @@ msgstr "" msgid "{}=\"{}\" not found in this project" msgstr "" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Contingut invàlid. Deu ser {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "" @@ -709,8 +709,8 @@ msgstr "Adreça d'email" msgid "comment" msgstr "Comentari" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -783,7 +783,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no és un arxiu json vàlid" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "El projecte no existeix" @@ -791,29 +792,66 @@ msgstr "El projecte no existeix" msgid "Bad signature" msgstr "Firma no vàlida." -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "El payload no és un application/x-www-form-urlencoded vàlid" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "El payload no és vàlid" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'element referenciat no existeix" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'estatus no existeix." -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Informació d'incidència no vàlida." + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Informació del comentari a l'incidència no vàlid." + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -821,15 +859,11 @@ msgid "" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." msgstr "" -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "" -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Informació d'incidència no vàlida." - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -840,15 +874,11 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "" -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Informació del comentari a l'incidència no vàlid." - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -859,7 +889,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -867,14 +897,33 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1045,11 +1094,11 @@ msgstr "" msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Al menys un del usuaris ha de ser administrador" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." @@ -1061,7 +1110,7 @@ msgstr "" msgid "Project ID not matches between object and project" msgstr "" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1069,8 +1118,8 @@ msgstr "" msgid "owner" msgstr "Amo" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1082,16 +1131,16 @@ msgstr "Amo" msgid "project" msgstr "Projecte" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "Tipus de contingut" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "Id d'objecte" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1099,16 +1148,16 @@ msgstr "Id d'objecte" msgid "modified date" msgstr "Data de modificació" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "Arxiu adjunt" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "està obsolet " -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1116,8 +1165,8 @@ msgstr "està obsolet " msgid "description" msgstr "Descripció" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1135,10 +1184,22 @@ msgid "Jitsi" msgstr "" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1148,20 +1209,25 @@ msgstr "" msgid "name" msgstr "Nom" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "tipus" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "història d'usuari" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "tasca" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "incidéncia" @@ -1298,23 +1364,27 @@ msgstr "contingut" msgid "blocked note" msgstr "nota de bloqueig" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tens permissos per a ficar aquest sprint a aquesta incidència" -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "No tens permissos per a ficar aquest status a aquesta tasca" -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "No tens permissos per a ficar aquesta severitat a aquesta tasca" -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "No tens permissos per a ficar aquesta prioritat a aquesta incidència" -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "No tens permissos per a ficar aquest tipus a aquesta incidència" @@ -1336,10 +1406,6 @@ msgstr "severitat" msgid "priority" msgstr "prioritat" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "tipus" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1494,8 +1560,8 @@ msgid "videoconference system" msgstr "sistema de videoconferència" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "sala videoconferència" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2119,51 +2185,51 @@ msgstr "Versió" msgid "You can't leave the project if there are no more owners" msgstr "No pots deixar el projecte si no hi ha més amos" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "Aquest e-mail ja està en ús" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Rol invàlid per al projecte" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Opcions per defecte" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Estatus d'històries d'usuari" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Punts" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Estatus de tasques" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Estatus d'incidéncies" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "Tipus d'incidéncies" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Prioritats" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Severitats" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Rols" @@ -2175,10 +2241,17 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "No tens permissos per a afegir/modificar aquesta tasca." +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2554,7 +2627,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 12fedc19..d1bef387 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-29 10:23+0000\n" -"Last-Translator: Regina \n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -574,20 +574,20 @@ msgstr "Fehler beim Importieren der Chroniken" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" wurde in diesem Projekt nicht gefunden" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Invalider Inhalt. Er muss wie folgt sein: {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" @@ -851,8 +851,8 @@ msgstr "E-Mail Adresse" msgid "comment" msgstr "Kommentar" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -925,7 +925,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Die Nutzlast ist kein gültiges json" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "Das Projekt existiert nicht" @@ -933,29 +934,66 @@ msgstr "Das Projekt existiert nicht" msgid "Bad signature" msgstr "Falsche Signatur" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "Die Nutzlast ist eine ungültige Anwendung/x-www-form-urlencoded" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "Die Nutzlast ist ungültig" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Das referenzierte Element existiert nicht" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Der Status existiert nicht" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "Der Status des BitBucket Commits hat sich geändert" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Ungültige Ticket-Information" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Ungültige Ticket-Kommentar Information" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -966,15 +1004,11 @@ msgstr "" "@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "Der Status des GitHub Commits hat sich geändert" -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Ungültige Ticket-Information" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -991,15 +1025,11 @@ msgstr "" "\n" " {description}" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "Ticket erstellt von GitHub." -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Ungültige Ticket-Kommentar Information" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -1016,7 +1046,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -1027,14 +1057,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "Der Status des GitLab Commits hat sich geändert" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "Erstellt von GitLab" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1205,11 +1254,11 @@ msgstr "Unglültiger Templatename" msgid "Not valid template description" msgstr "Ungültige Templatebeschreibung" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Mindestens ein Benutzer muss ein aktiver Administrator sein. " -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" @@ -1221,7 +1270,7 @@ msgstr "Es werden nur Teilaktualisierungen unterstützt" msgid "Project ID not matches between object and project" msgstr "Nr. unterschreidet sich zwischen dem Objekt und dem Projekt" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1229,8 +1278,8 @@ msgstr "Nr. unterschreidet sich zwischen dem Objekt und dem Projekt" msgid "owner" msgstr "Besitzer" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1242,16 +1291,16 @@ msgstr "Besitzer" msgid "project" msgstr "Projekt" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "Inhaltsart" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "Objekt Nr." -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1259,16 +1308,16 @@ msgstr "Objekt Nr." msgid "modified date" msgstr "Zeitpunkt der Änderung" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "Angehangene Datei" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "wurde verworfen" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1276,8 +1325,8 @@ msgstr "wurde verworfen" msgid "description" msgstr "Beschreibung" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1295,10 +1344,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Gesprächig" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1308,20 +1369,25 @@ msgstr "Gesprächig" msgid "name" msgstr "Name" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "Art" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "Werte" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "User-Story" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "Aufgabe" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "Ticket" @@ -1458,27 +1524,31 @@ msgstr "Inhalt" msgid "blocked note" msgstr "Blockierungsgrund" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Sprint zu setzen." -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Status zu setzen. " -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Gewichtung zu setzen." -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Priorität zu setzen. " -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "Sie haben nicht die Berechtigung, das Ticket auf diese Art zu setzen." @@ -1500,10 +1570,6 @@ msgstr "Gewichtung" msgid "priority" msgstr "Priorität" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "Art" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1658,7 +1724,7 @@ msgid "videoconference system" msgstr "Videokonferenzsystem" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" +msgid "videoconference extra data" msgstr "" #: taiga/projects/models.py:159 @@ -2563,51 +2629,51 @@ msgstr "" "Sie können das Projekt nicht verlassen, wenn keine weiteren Besitzer " "vorhanden sind" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "Die E-Mailadresse ist bereits vergeben" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Ungültige Rolle für dieses Projekt" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "Die Gesamtzahl der Meilensteine muss größer oder gleich Null sein " -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Voreingestellte Optionen" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Status für User-Stories" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Punkte" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Aufgaben Status" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Ticket Status" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "Ticket Arten" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Prioritäten" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Gewichtung" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Rollen" @@ -2619,11 +2685,17 @@ msgstr "Zukünftiger Sprint" msgid "Project End" msgstr "Projektende" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." msgstr "" -"Sie haben nicht die Berechtigungen, um diese Aufgabe hinzuzufügen/zu ändern." #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -3016,7 +3088,15 @@ msgstr "Projekteigentümer " msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index ce6bab28..4209e646 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -514,20 +514,20 @@ msgstr "" msgid "{}=\"{}\" not found in this project" msgstr "" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "" -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "" @@ -698,8 +698,8 @@ msgstr "" msgid "comment" msgstr "" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -756,7 +756,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "" @@ -764,29 +765,66 @@ msgstr "" msgid "Bad signature" msgstr "" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -794,15 +832,11 @@ msgid "" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." msgstr "" -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "" -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -813,15 +847,11 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "" -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -832,7 +862,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -840,14 +870,33 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1018,11 +1067,11 @@ msgstr "" msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "" @@ -1034,7 +1083,7 @@ msgstr "" msgid "Project ID not matches between object and project" msgstr "" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1042,8 +1091,8 @@ msgstr "" msgid "owner" msgstr "" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1055,16 +1104,16 @@ msgstr "" msgid "project" msgstr "" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1072,16 +1121,16 @@ msgstr "" msgid "modified date" msgstr "" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1089,8 +1138,8 @@ msgstr "" msgid "description" msgstr "" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1108,10 +1157,22 @@ msgid "Jitsi" msgstr "" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1121,20 +1182,25 @@ msgstr "" msgid "name" msgstr "" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "" @@ -1271,23 +1337,27 @@ msgstr "" msgid "blocked note" msgstr "" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "" -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "" -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "" -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "" -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "" @@ -1309,10 +1379,6 @@ msgstr "" msgid "priority" msgstr "" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1467,7 +1533,7 @@ msgid "videoconference system" msgstr "" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" +msgid "videoconference extra data" msgstr "" #: taiga/projects/models.py:159 @@ -2086,51 +2152,51 @@ msgstr "" msgid "You can't leave the project if there are no more owners" msgstr "" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "" @@ -2142,9 +2208,16 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." msgstr "" #: taiga/projects/tasks/models.py:56 @@ -2503,7 +2576,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index f8c322b1..883315a9 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-17 07:24+0000\n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:16+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" "language/es/)\n" @@ -563,20 +563,20 @@ msgstr "error importando los timelines" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" no se ha encontrado en este proyecto" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Contenido inválido. Debe ser {\"clave\": \"valor\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" @@ -835,8 +835,8 @@ msgstr "dirección de email" msgid "comment" msgstr "comentario" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -908,7 +908,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no es un json válido" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "El proyecto no existe" @@ -916,29 +917,66 @@ msgstr "El proyecto no existe" msgid "Bad signature" msgstr "Firma errónea" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "El payload no es una application/x-www-form-urlencoded válida" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "El payload no es válido" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "El elemento referenciado no existe" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "El estado no existe" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "Estado cambiado desde un commit de BitBucket" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Información inválida de Issue" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Información de comentario de Issue inválida" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -950,15 +988,11 @@ msgstr "" "de GitHub [{commit_id}]({commit_url} \"Ver commit '{commit_id} - " "{commit_message}'\")." -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "Estado cambiado a través de un commit en GitHub." -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Información inválida de Issue" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -974,15 +1008,11 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "Petición creada a través de GitHub." -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Información de comentario de Issue inválida" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -998,7 +1028,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -1009,14 +1039,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "Estado cambiado desde un commit de GitLab" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "Creado desde Gitlab" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1187,11 +1236,11 @@ msgstr "Nombre de plantilla invalido" msgid "Not valid template description" msgstr "Descripción de plantilla invalida" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Al menos uno de los usuario debe ser un administrador." -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." @@ -1203,7 +1252,7 @@ msgstr "La actualización parcial no está soportada." msgid "Project ID not matches between object and project" msgstr "El ID de proyecto no coincide entre el adjunto y un proyecto" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1211,8 +1260,8 @@ msgstr "El ID de proyecto no coincide entre el adjunto y un proyecto" msgid "owner" msgstr "Dueño" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1224,16 +1273,16 @@ msgstr "Dueño" msgid "project" msgstr "Proyecto" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "típo de contenido" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "id de objeto" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1241,16 +1290,16 @@ msgstr "id de objeto" msgid "modified date" msgstr "fecha modificada" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "archivo adjunto" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "está desactualizado" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1258,8 +1307,8 @@ msgstr "está desactualizado" msgid "description" msgstr "descripción" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1277,10 +1326,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "Personalizado" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "Texto" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "Texto multilínea" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1290,20 +1351,25 @@ msgstr "Talky" msgid "name" msgstr "nombre" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "tipo" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "valores" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "historia de usuario" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "tarea" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "petición" @@ -1440,23 +1506,27 @@ msgstr "contenido" msgid "blocked note" msgstr "nota de bloqueo" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "sprint" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tienes permisos para asignar un sprint a esta petición." -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "No tienes permisos para asignar un estado a esta petición." -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "No tienes permisos para establecer la gravedad de esta petición." -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "No tienes permiso para establecer la prioridad de esta petición." -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "No tienes permiso para establecer el tipo de esta petición." @@ -1478,10 +1548,6 @@ msgstr "gravedad" msgid "priority" msgstr "prioridad" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "tipo" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1638,8 +1704,8 @@ msgid "videoconference system" msgstr "sistema de videoconferencia" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "salt de la sala de videoconferencia" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2495,51 +2561,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "No puedes abandonar este proyecto si no existen mas propietarios del mismo" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "La dirección de email ya está en uso." -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Rol inválido para el proyecto" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "El número total de sprints debe ser mayor o igual a cero" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Opciones por defecto" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Estados de historia de usuario" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Puntos" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Estado de tareas" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Estados de peticion" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "Tipos de petición" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Gravedades" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Roles" @@ -2551,10 +2617,17 @@ msgstr "Sprint futuro" msgid "Project End" msgstr "Final de proyecto" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "No tienes permisos para añadir/modificar esta tarea." +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2964,7 +3037,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index d38113fe..3d12be93 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-09 07:47+0000\n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" "language/fi/)\n" @@ -550,20 +550,20 @@ msgstr "virhe aikajanojen tuonnissa" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" ei löytynyt tästä projektista" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Virheellinen sisältä, pitää olla muodossa {\"avain\": \"arvo\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" @@ -819,8 +819,8 @@ msgstr "sähköpostiosoite" msgid "comment" msgstr "kommentti" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -894,7 +894,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "The payload is not a valid json" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "Projektia ei löydy" @@ -902,29 +903,66 @@ msgstr "Projektia ei löydy" msgid "Bad signature" msgstr "Virheellinen allekirjoitus" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "The payload is not a valid application/x-www-form-urlencoded" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "The payload is not valid" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Viitattu elementtiä ei löydy" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Tilaa ei löydy" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "Tila muutettu BitBucket kommitilla" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Virheellinen pyynnön tieto" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Virheellinen pyynnön kommentin tieto" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -935,15 +973,11 @@ msgstr "" "@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "Tila muutettu GitHub commitin toimesta." -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Virheellinen pyynnön tieto" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -960,15 +994,11 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "Pyyntö luotu GitHubista" -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Virheellinen pyynnön kommentin tieto" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -985,7 +1015,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -996,14 +1026,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "Tila muutettu GitLab kommitilla" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "Luotu GitLabissa" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1174,11 +1223,11 @@ msgstr "Virheellinen mallipohjan nimi" msgid "Not valid template description" msgstr "Virheellinen mallipohjan kuvaus" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Vähintään yhden käyttäjän pitää olla aktiivinen ylläpitäjä" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." @@ -1190,7 +1239,7 @@ msgstr "Osittaiset päivitykset eivät ole tuettuna." msgid "Project ID not matches between object and project" msgstr "Projekti ID ei vastaa kohdetta ja projektia" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1198,8 +1247,8 @@ msgstr "Projekti ID ei vastaa kohdetta ja projektia" msgid "owner" msgstr "omistaja" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1211,16 +1260,16 @@ msgstr "omistaja" msgid "project" msgstr "projekti" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "sisältötyyppi" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "objekti ID" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1228,16 +1277,16 @@ msgstr "objekti ID" msgid "modified date" msgstr "muokkauspvm" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "liite" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "on poistettu" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1245,8 +1294,8 @@ msgstr "on poistettu" msgid "description" msgstr "kuvaus" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1264,10 +1313,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1277,20 +1338,25 @@ msgstr "Talky" msgid "name" msgstr "nimi" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "tyyppi" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "arvot" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "käyttäjätarina" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "tehtävä" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "pyyntö" @@ -1427,23 +1493,27 @@ msgstr "sisältö" msgid "blocked note" msgstr "suljettu muistiinpano" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "Sinulla ei ole oikeuksia laittaa kierrosta tälle pyynnölle." -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "Sinulla ei ole oikeutta asettaa statusta tälle pyyntö." -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "Sinulla ei ole oikeutta asettaa vakavuutta tälle pyynnölle." -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "Sinulla ei ole oikeutta asettaa kiireellisyyttä tälle pyynnölle." -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "Sinulla ei ole oikeutta asettaa tyyppiä tälle pyyntö." @@ -1465,10 +1535,6 @@ msgstr "vakavuus" msgid "priority" msgstr "kiireellisyys" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "tyyppi" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1623,8 +1689,8 @@ msgid "videoconference system" msgstr "videokokous järjestelmä" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "videokokousjärjestelmän suola" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2488,51 +2554,51 @@ msgstr "versio" msgid "You can't leave the project if there are no more owners" msgstr "Et voi jättää projektia, jos olet ainoa omistaja" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "Sähköpostiosoite on jo käytössä" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Virheellinen rooli projektille" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "Virstapylväitä yhteensä pitää olla vähintään 0." -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Oletusoptiot" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Käyttäjätarinatilat" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Pisteet" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Tehtävien tilat" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Pyyntöjen tilat" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "pyyntötyypit" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Kiireellisyydet" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Vakavuudet" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Roolit" @@ -2544,10 +2610,17 @@ msgstr "Tuleva kierros" msgid "Project End" msgstr "Projektin loppu" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "Sinulla ei ole oikeuksia lisätä tai muokata tätä tehtävää." +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2954,7 +3027,15 @@ msgstr "Tuoteomistaja" msgid "Stakeholder" msgstr "Sidosryhmä" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index a5de978e..1cdd658c 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-12 21:30+0000\n" -"Last-Translator: Nlko \n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" "language/fr/)\n" "MIME-Version: 1.0\n" @@ -558,20 +558,20 @@ msgstr "erreur lors de l'import des timelines" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" non trouvé dans the projet" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Format non valide. Il doit être de la forme {\"cle\": \"valeur\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" @@ -742,8 +742,8 @@ msgstr "Adresse email" msgid "comment" msgstr "Commentaire" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -815,7 +815,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Le payload n'est pas un json valide" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "Le projet n'existe pas" @@ -823,29 +824,66 @@ msgstr "Le projet n'existe pas" msgid "Bad signature" msgstr "Signature non valide" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "Le payload n'est pas au format application/x-www-form-urlencoded" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "Le payload n'est pas valide" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'élément référencé n'existe pas" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'état n'existe pas" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "Statut changé depuis un commit BitBucket" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Information incorrecte sur le problème" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Ignoré" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -853,15 +891,11 @@ msgid "" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." msgstr "" -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "Statut changé depuis un commit GitHub." -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Information incorrecte sur le problème" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -872,15 +906,11 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "Suivi de problème créé à partir de GitHub." -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Ignoré" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -891,7 +921,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -902,14 +932,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "Statut changé depuis un commit GitLab" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "Créé à partir de GitLab" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1080,11 +1129,11 @@ msgstr "Nom de modèle non valide" msgid "Not valid template description" msgstr "Description du modèle non valide" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Au moins un utilisateur doit être un administrateur actif" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" @@ -1096,7 +1145,7 @@ msgstr "Les mises à jour non partielles ne sont pas supportées" msgid "Project ID not matches between object and project" msgstr "L'identifiant du projet de correspond pas entre l'objet et le projet" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1104,8 +1153,8 @@ msgstr "L'identifiant du projet de correspond pas entre l'objet et le projet" msgid "owner" msgstr "propriétaire" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1117,16 +1166,16 @@ msgstr "propriétaire" msgid "project" msgstr "projet" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "type du contenu" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "identifiant de l'objet" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1134,16 +1183,16 @@ msgstr "identifiant de l'objet" msgid "modified date" msgstr "état modifié" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "pièces jointes" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "est obsolète" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1151,8 +1200,8 @@ msgstr "est obsolète" msgid "description" msgstr "description" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1170,10 +1219,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1183,20 +1244,25 @@ msgstr "Talky" msgid "name" msgstr "nom" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "type" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "valeurs" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "histoire utilisateur" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "tâche" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "problème" @@ -1333,23 +1399,27 @@ msgstr "contenu" msgid "blocked note" msgstr "note bloquée" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "Vous n'avez pas la permission d'affecter ce sprint à ce problème." -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "Vous n'avez pas la permission d'affecter ce statut à ce problème." -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "Vous n'avez pas la permission d'affecter cette sévérité à ce problème." -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "Vous n'avez pas la permission d'affecter cette priorité à ce problème." -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "Vous n'avez pas la permission d'affecter ce type à ce problème." @@ -1371,10 +1441,6 @@ msgstr "sévérité" msgid "priority" msgstr "priorité" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "type" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1529,8 +1595,8 @@ msgid "videoconference system" msgstr "plateforme de vidéoconférence" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "salt pour la salle de vidéoconférence" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2167,51 +2233,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "Vous ne pouvez pas quitter le projet si il n'y a plus d'autres propriétaires" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "Adresse email déjà existante" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Rôle non valide pour le projet" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "Le nombre de jalons doit être supérieur ou égal à zéro" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Options par défaut" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Etats de la User Story" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Points" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Etats des tâches" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Statuts des problèmes" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "Types de problèmes" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Priorités" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Sévérités" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Rôles" @@ -2223,10 +2289,17 @@ msgstr "Sprint futurs" msgid "Project End" msgstr "Fin du projet" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "Vous n'avez pas les permissions pour ajouter / modifier cette tâche" +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2613,7 +2686,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Participant" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 822ec21a..e5f609ad 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-09 07:47+0000\n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" "language/nl/)\n" @@ -563,20 +563,20 @@ msgstr "fout bij importeren tijdlijnen" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" niet gevonden in dit project" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Ongeldige inhoud. Volgend formaat geldt {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" @@ -769,8 +769,8 @@ msgstr "e-mail adres" msgid "comment" msgstr "commentaar" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -843,7 +843,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "De payload is geen geldige json" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "Het project bestaat niet" @@ -851,29 +852,66 @@ msgstr "Het project bestaat niet" msgid "Bad signature" msgstr "Slechte signature" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "De payload is geen geldige application/x-www-form-urlencoded" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "De payload is niet geldig" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Het element waarnaar verwezen wordt bestaat niet" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "De status bestaat niet" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "Status veranderd door Bitbucket commit" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Ongeldige issue informatie" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Ongeldige issue commentaar informatie" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -881,15 +919,11 @@ msgid "" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." msgstr "" -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "Status veranderd door GitHub commit." -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "Ongeldige issue informatie" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -900,15 +934,11 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "Issue aangemaakt via GitHub." -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "Ongeldige issue commentaar informatie" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -919,7 +949,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -930,14 +960,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "Status veranderd door GitLab commit" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "Aangemaakt via GitLab" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1108,11 +1157,11 @@ msgstr "Ongeldige template naam" msgid "Not valid template description" msgstr "Ongeldige template omschrijving" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Minstens één van de gebruikers moet een active admin zijn" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." @@ -1124,7 +1173,7 @@ msgstr "Niet-partiële updates worden niet ondersteund." msgid "Project ID not matches between object and project" msgstr "Project ID van object is niet gelijk aan die van het project" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1132,8 +1181,8 @@ msgstr "Project ID van object is niet gelijk aan die van het project" msgid "owner" msgstr "eigenaar" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1145,16 +1194,16 @@ msgstr "eigenaar" msgid "project" msgstr "project" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "inhoud type" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "object id" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1162,16 +1211,16 @@ msgstr "object id" msgid "modified date" msgstr "gemodifieerde datum" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "bijgevoegd bestand" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "is verouderd" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1179,8 +1228,8 @@ msgstr "is verouderd" msgid "description" msgstr "omschrijving" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1198,10 +1247,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1211,20 +1272,25 @@ msgstr "Talky" msgid "name" msgstr "naam" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "type" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "waarden" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "user story" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "taak" -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "issue" @@ -1361,25 +1427,29 @@ msgstr "inhoud" msgid "blocked note" msgstr "geblokkeerde notitie" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "Je hebt geen toestemming om deze sprint op deze issue te zetten." -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "Je hebt geen toestemming om deze status toe te kennen aan dze issue." -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Je hebt geen toestemming om dit ernstniveau toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Je hebt geen toestemming om deze prioriteit toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "Je hebt geen toestemming om dit type toe te kennen aan deze issue." @@ -1401,10 +1471,6 @@ msgstr "erstniveau" msgid "priority" msgstr "prioriteit" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "type" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1559,8 +1625,8 @@ msgid "videoconference system" msgstr "videoconference systeem" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "videoconference kamer salt" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2208,51 +2274,51 @@ msgstr "versie" msgid "You can't leave the project if there are no more owners" msgstr "Je kan het project niet verlaten als er geen andere eigenaars zijn" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "E-mail adres is al in gebruik" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "Ongeldige rol voor project" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "Totaal milestones moet groter of gelijk zijn aan 0" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "Standaard opties" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "Status van User story" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "Punten" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "Statussen van taken" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "Statussen van Issues" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "Types van issue" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "Prioriteiten" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "Ernstniveaus" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "Rollen" @@ -2264,10 +2330,17 @@ msgstr "Toekomstige sprint" msgid "Project End" msgstr "Project einde" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "Je hebt geen toestemming om deze taak toe te voegen/te wijzigen" +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2649,7 +2722,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 7eae5e43..8d8c70c2 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-06-15 12:34+0200\n" -"PO-Revision-Date: 2015-06-27 02:13+0000\n" -"Last-Translator: Chi-Hsun Tsai \n" +"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" @@ -555,20 +555,20 @@ msgstr "滙入時間軸出錯" msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" 無法在此專案中找到" -#: taiga/export_import/serializers.py:382 +#: taiga/export_import/serializers.py:384 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "無效內容。必須為 {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:397 +#: taiga/export_import/serializers.py:399 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" -#: taiga/export_import/serializers.py:466 +#: taiga/export_import/serializers.py:468 #: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:66 taiga/projects/serializers.py:92 -#: taiga/projects/serializers.py:122 taiga/projects/serializers.py:164 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" @@ -824,8 +824,8 @@ msgstr "電子郵件" msgid "comment" msgstr "評論" -#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:63 -#: taiga/projects/custom_attributes/models.py:38 +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 #: taiga/projects/models.py:129 taiga/projects/models.py:561 #: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 @@ -895,7 +895,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "載荷為無效json" -#: taiga/hooks/api.py:61 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 msgid "The project doesn't exist" msgstr "專案不存在" @@ -903,29 +904,66 @@ msgstr "專案不存在" msgid "Bad signature" msgstr "錯誤簽名" -#: taiga/hooks/bitbucket/api.py:40 -msgid "The payload is not a valid application/x-www-form-urlencoded" -msgstr "載荷為無效應用 /x-www-form-urlencoded" - -#: taiga/hooks/bitbucket/event_hooks.py:45 -msgid "The payload is not valid" -msgstr "載荷無效" - -#: taiga/hooks/bitbucket/event_hooks.py:81 -#: taiga/hooks/github/event_hooks.py:76 taiga/hooks/gitlab/event_hooks.py:74 +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "參考元素不存在" -#: taiga/hooks/bitbucket/event_hooks.py:88 -#: taiga/hooks/github/event_hooks.py:83 taiga/hooks/gitlab/event_hooks.py:81 +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "狀態不存在" -#: taiga/hooks/bitbucket/event_hooks.py:94 +#: taiga/hooks/bitbucket/event_hooks.py:86 msgid "Status changed from BitBucket commit" msgstr "來自BitBucket 投入的狀態更新" -#: taiga/hooks/github/event_hooks.py:97 +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "無效的問題資訊" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "無效的議題評論資訊" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/github/event_hooks.py:96 #, python-brace-format msgid "" "Status changed by [@{github_user_name}]({github_user_url} \"See " @@ -936,15 +974,11 @@ msgstr "" "@{github_user_name}'s GitHub profile\") 來自GitHub之投入 [{commit_id}]" "({commit_url} \"See commit '{commit_id} - {commit_message}'\")." -#: taiga/hooks/github/event_hooks.py:108 +#: taiga/hooks/github/event_hooks.py:107 msgid "Status changed from GitHub commit." msgstr "來自GitHub投入的狀態更新" -#: taiga/hooks/github/event_hooks.py:142 taiga/hooks/gitlab/event_hooks.py:114 -msgid "Invalid issue information" -msgstr "無效的問題資訊" - -#: taiga/hooks/github/event_hooks.py:158 +#: taiga/hooks/github/event_hooks.py:157 #, python-brace-format msgid "" "Issue created by [@{github_user_name}]({github_user_url} \"See " @@ -959,15 +993,11 @@ msgstr "" "[gh#{number} - {subject}]({github_url} ”跳至 'gh#{number} - {subject}'\") \n" "{description}" -#: taiga/hooks/github/event_hooks.py:169 +#: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." msgstr "自來GitHub 的問題 " -#: taiga/hooks/github/event_hooks.py:178 taiga/hooks/github/event_hooks.py:193 -msgid "Invalid issue comment information" -msgstr "無效的議題評論資訊" - -#: taiga/hooks/github/event_hooks.py:201 +#: taiga/hooks/github/event_hooks.py:200 #, python-brace-format msgid "" "Comment by [@{github_user_name}]({github_user_url} \"See " @@ -983,7 +1013,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/github/event_hooks.py:212 +#: taiga/hooks/github/event_hooks.py:211 #, python-brace-format msgid "" "Comment From GitHub:\n" @@ -994,14 +1024,33 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/gitlab/event_hooks.py:87 +#: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" msgstr "來自GitLab提供的狀態變更" -#: taiga/hooks/gitlab/event_hooks.py:129 +#: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" msgstr "創建立GitLab" +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" + #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 msgid "View project" @@ -1172,11 +1221,11 @@ msgstr "非有效樣板名稱 " msgid "Not valid template description" msgstr "無效樣板描述" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:257 +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "至少需有一位使用者擔任管理員" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:505 msgid "You don't have permisions to see that." msgstr "您無觀看權限" @@ -1188,7 +1237,7 @@ msgstr "不支援非部份更新" msgid "Project ID not matches between object and project" msgstr "專案ID不符合物件與專案" -#: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38 +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 @@ -1196,8 +1245,8 @@ msgstr "專案ID不符合物件與專案" msgid "owner" msgstr "所有者" -#: taiga/projects/attachments/models.py:56 -#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 #: taiga/projects/models.py:338 taiga/projects/models.py:364 #: taiga/projects/models.py:395 taiga/projects/models.py:424 @@ -1209,16 +1258,16 @@ msgstr "所有者" msgid "project" msgstr "專案" -#: taiga/projects/attachments/models.py:58 +#: taiga/projects/attachments/models.py:56 msgid "content type" msgstr "內容類型" -#: taiga/projects/attachments/models.py:60 +#: taiga/projects/attachments/models.py:58 msgid "object id" msgstr "物件ID" -#: taiga/projects/attachments/models.py:66 -#: taiga/projects/custom_attributes/models.py:40 +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:132 taiga/projects/models.py:564 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 @@ -1226,16 +1275,16 @@ msgstr "物件ID" msgid "modified date" msgstr "修改日期" -#: taiga/projects/attachments/models.py:71 +#: taiga/projects/attachments/models.py:69 msgid "attached file" msgstr "附加檔案" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:72 msgid "is deprecated" msgstr "棄用" -#: taiga/projects/attachments/models.py:75 -#: taiga/projects/custom_attributes/models.py:32 +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 #: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 @@ -1243,8 +1292,8 @@ msgstr "棄用" msgid "description" msgstr "描述" -#: taiga/projects/attachments/models.py:76 -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 #: taiga/projects/models.py:391 taiga/projects/models.py:418 #: taiga/projects/models.py:453 taiga/projects/models.py:476 @@ -1262,10 +1311,22 @@ msgid "Jitsi" msgstr "Jitsi" #: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "" + +#: taiga/projects/choices.py:24 msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:31 +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "" + +#: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 #: taiga/projects/models.py:350 taiga/projects/models.py:389 #: taiga/projects/models.py:414 taiga/projects/models.py:451 @@ -1275,20 +1336,25 @@ msgstr "Talky" msgid "name" msgstr "姓名" -#: taiga/projects/custom_attributes/models.py:81 +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "類型" + +#: taiga/projects/custom_attributes/models.py:87 msgid "values" msgstr "價值" -#: taiga/projects/custom_attributes/models.py:91 +#: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" msgstr "使用者故事" -#: taiga/projects/custom_attributes/models.py:106 +#: taiga/projects/custom_attributes/models.py:112 msgid "task" msgstr "任務 " -#: taiga/projects/custom_attributes/models.py:121 +#: taiga/projects/custom_attributes/models.py:127 msgid "issue" msgstr "問題 " @@ -1425,23 +1491,27 @@ msgstr "內容" msgid "blocked note" msgstr "封鎖筆記" -#: taiga/projects/issues/api.py:139 +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "" + +#: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." msgstr "您無權限設定此問題的衝刺任務" -#: taiga/projects/issues/api.py:143 +#: taiga/projects/issues/api.py:199 msgid "You don't have permissions to set this status to this issue." msgstr "您無權限設定此問題的狀態" -#: taiga/projects/issues/api.py:147 +#: taiga/projects/issues/api.py:203 msgid "You don't have permissions to set this severity to this issue." msgstr "您無權限設定此問題的嚴重性" -#: taiga/projects/issues/api.py:151 +#: taiga/projects/issues/api.py:207 msgid "You don't have permissions to set this priority to this issue." msgstr "您無權限設定此問題的優先性" -#: taiga/projects/issues/api.py:155 +#: taiga/projects/issues/api.py:211 msgid "You don't have permissions to set this type to this issue." msgstr "您無權限設定此問題的類型" @@ -1463,10 +1533,6 @@ msgstr "嚴重性" msgid "priority" msgstr "優先性" -#: taiga/projects/issues/models.py:46 -msgid "type" -msgstr "類型" - #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" @@ -1621,8 +1687,8 @@ msgid "videoconference system" msgstr "視訊會議系統" #: taiga/projects/models.py:154 taiga/projects/models.py:581 -msgid "videoconference room salt" -msgstr "視訊會議房" +msgid "videoconference extra data" +msgstr "" #: taiga/projects/models.py:159 msgid "creation template" @@ -2490,51 +2556,51 @@ msgstr "版本" msgid "You can't leave the project if there are no more owners" msgstr "如果專案無所有者,你將無法脫離該專案" -#: taiga/projects/serializers.py:233 +#: taiga/projects/serializers.py:237 msgid "Email address is already taken" msgstr "電子郵件已使用" -#: taiga/projects/serializers.py:245 +#: taiga/projects/serializers.py:249 msgid "Invalid role for the project" msgstr "專案無效的角色" -#: taiga/projects/serializers.py:340 +#: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" msgstr "Kanban" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:405 msgid "Default options" msgstr "預設選項" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:406 msgid "User story's statuses" msgstr "使用者故事狀態" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:407 msgid "Points" msgstr "點數" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:408 msgid "Task's statuses" msgstr "任務狀態" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:409 msgid "Issue's statuses" msgstr "問題狀態" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:410 msgid "Issue's types" msgstr "問題類型" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:411 msgid "Priorities" msgstr "優先性" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:412 msgid "Severities" msgstr "嚴重性" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:413 msgid "Roles" msgstr "角色" @@ -2546,10 +2612,17 @@ msgstr "未來之衝刺" msgid "Project End" msgstr "專案結束" -#: taiga/projects/tasks/api.py:58 taiga/projects/tasks/api.py:61 -#: taiga/projects/tasks/api.py:64 taiga/projects/tasks/api.py:67 -msgid "You don't have permissions for add/modify this task." -msgstr "您無新增或更改此任務的權限" +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2949,7 +3022,15 @@ msgstr "產品所有人" msgid "Stakeholder" msgstr "利害關係人" -#: taiga/projects/userstories/api.py:174 +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:212 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " From df9317cb4a6fb5acea51956df29810d24312a3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Mon, 6 Jul 2015 12:08:49 +0200 Subject: [PATCH 055/190] Fix gilab migration --- taiga/hooks/gitlab/migrations/0001_initial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/hooks/gitlab/migrations/0001_initial.py b/taiga/hooks/gitlab/migrations/0001_initial.py index 8002d965..4698c158 100644 --- a/taiga/hooks/gitlab/migrations/0001_initial.py +++ b/taiga/hooks/gitlab/migrations/0001_initial.py @@ -29,7 +29,7 @@ def create_github_system_user(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('users', '0006_auto_20141030_1132') + ('users', '0011_user_theme') ] operations = [ From 31a8e0749f5ba533b0e27721ec8ba920c40f2f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 6 Jul 2015 13:59:40 +0200 Subject: [PATCH 056/190] Fix some texts --- taiga/hooks/bitbucket/event_hooks.py | 8 ++++---- taiga/locale/en/LC_MESSAGES/django.po | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 5aa86fde..75b2974e 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -130,8 +130,8 @@ class IssuesEventHook(BaseEventHook): if number and subject and bitbucket_user_name and bitbucket_user_url: comment = _("Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} " "\"See @{bitbucket_user_name}'s BitBucket profile\") " - "from BitBucket.\nOrigin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} " - "\"Go to 'gh#{number} - {subject}'\"):\n\n" + "from BitBucket.\nOrigin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} " + "\"Go to 'bb#{number} - {subject}'\"):\n\n" "{description}").format(bitbucket_user_name=bitbucket_user_name, bitbucket_user_url=bitbucket_user_url, number=number, @@ -173,8 +173,8 @@ class IssueCommentEventHook(BaseEventHook): if number and subject and bitbucket_user_name and bitbucket_user_url: comment = _("Comment by [@{bitbucket_user_name}]({bitbucket_user_url} " "\"See @{bitbucket_user_name}'s BitBucket profile\") " - "from BitBucket.\nOrigin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} " - "\"Go to 'gh#{number} - {subject}'\")\n\n" + "from BitBucket.\nOrigin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} " + "\"Go to 'bb#{number} - {subject}'\")\n\n" "{message}").format(bitbucket_user_name=bitbucket_user_name, bitbucket_user_url=bitbucket_user_url, number=number, diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 4209e646..17255b38 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" +"POT-Creation-Date: 2015-07-06 13:53+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -789,8 +789,8 @@ msgstr "" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -810,8 +810,8 @@ msgstr "" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" From 99c0e21757521308f52dda439391a6ed42588317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 6 Jul 2015 16:07:38 +0200 Subject: [PATCH 057/190] Fix more texts --- taiga/hooks/gitlab/event_hooks.py | 4 ++-- taiga/locale/en/LC_MESSAGES/django.po | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/taiga/hooks/gitlab/event_hooks.py b/taiga/hooks/gitlab/event_hooks.py index fdc83066..83760d25 100644 --- a/taiga/hooks/gitlab/event_hooks.py +++ b/taiga/hooks/gitlab/event_hooks.py @@ -159,8 +159,8 @@ class IssueCommentEventHook(BaseEventHook): if number and subject and gitlab_user_name and gitlab_user_url: comment = _("Comment by [@{gitlab_user_name}]({gitlab_user_url} " "\"See @{gitlab_user_name}'s GitLab profile\") " - "from GitLab.\nOrigin GitLab issue: [gh#{number} - {subject}]({gitlab_url} " - "\"Go to 'gh#{number} - {subject}'\")\n\n" + "from GitLab.\nOrigin GitLab issue: [gl#{number} - {subject}]({gitlab_url} " + "\"Go to 'gl#{number} - {subject}'\")\n\n" "{message}").format(gitlab_user_name=gitlab_user_name, gitlab_user_url=gitlab_user_url, number=number, diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 17255b38..5c250211 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 13:53+0200\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -883,8 +883,8 @@ msgstr "" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" From 9d54dc63b52b1c5222a904c01ce272169dd26759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 6 Jul 2015 16:44:12 +0200 Subject: [PATCH 058/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 16 +++---- taiga/locale/de/LC_MESSAGES/django.po | 16 +++---- taiga/locale/es/LC_MESSAGES/django.po | 52 ++++++++++++++++------ taiga/locale/fi/LC_MESSAGES/django.po | 16 +++---- taiga/locale/fr/LC_MESSAGES/django.po | 16 +++---- taiga/locale/nl/LC_MESSAGES/django.po | 16 +++---- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 16 +++---- 7 files changed, 87 insertions(+), 61 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 8297fd47..8ed89dce 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" "language/ca/)\n" @@ -816,8 +816,8 @@ msgstr "Informació d'incidència no vàlida." msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -837,8 +837,8 @@ msgstr "Informació del comentari a l'incidència no vàlid." msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -910,8 +910,8 @@ msgstr "" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index d1bef387..6fe59434 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -13,8 +13,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" "language/de/)\n" @@ -958,8 +958,8 @@ msgstr "Ungültige Ticket-Information" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -979,8 +979,8 @@ msgstr "Ungültige Ticket-Kommentar Information" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -1070,8 +1070,8 @@ msgstr "Erstellt von GitLab" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 883315a9..1a86b943 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:16+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:16+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" "language/es/)\n" @@ -941,15 +941,21 @@ msgstr "Información inválida de Issue" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" +"Petición creada por [@{bitbucket_user_name}]({bitbucket_user_url} \"Ver el " +"perfil de @{bitbucket_user_name} en BitBucket\") desde BitBucket.\n" +"Petición de origen en BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Ir a 'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" #: taiga/hooks/bitbucket/event_hooks.py:142 msgid "Issue created from BitBucket." -msgstr "" +msgstr "Petición creada desde BitBucket." #: taiga/hooks/bitbucket/event_hooks.py:166 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 @@ -962,11 +968,17 @@ msgstr "Información de comentario de Issue inválida" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" +"Comentario de [@{bitbucket_user_name}]({bitbucket_user_url} \"\"Ver el " +"perfil de @{bitbucket_user_name} en BitBucket\") desde BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" #: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format @@ -975,6 +987,9 @@ msgid "" "\n" "{message}" msgstr "" +"Comentario desde BitBucket:\n" +"\n" +"{message}" #: taiga/hooks/github/event_hooks.py:96 #, python-brace-format @@ -1052,11 +1067,17 @@ msgstr "Creado desde Gitlab" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" +"Comentario de [@{gitlab_user_name}]({gitlab_user_url} \"Ver el perfil de " +"@{gitlab_user_name}'s en GitLab\") desde GitLab.\n" +"Petición de origen de GitLab: [gl#{number} - {subject}]({gitlab_url} \"Ir a " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" #: taiga/hooks/gitlab/event_hooks.py:171 #, python-brace-format @@ -1065,6 +1086,9 @@ msgid "" "\n" "{message}" msgstr "" +"Comentario desde GitLab:\n" +"\n" +"{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 @@ -1705,7 +1729,7 @@ msgstr "sistema de videoconferencia" #: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference extra data" -msgstr "" +msgstr "datos extra de videoconferencia" #: taiga/projects/models.py:159 msgid "creation template" @@ -2619,15 +2643,15 @@ msgstr "Final de proyecto" #: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 msgid "You don't have permissions to set this sprint to this task." -msgstr "" +msgstr "No tienes permisos para asignar este sprint a esta tarea." #: taiga/projects/tasks/api.py:93 msgid "You don't have permissions to set this user story to this task." -msgstr "" +msgstr "No tienes permisos para asignar esta historia a esta tarea." #: taiga/projects/tasks/api.py:96 msgid "You don't have permissions to set this status to this task." -msgstr "" +msgstr "No tienes permisos para asignar este estado a esta tarea." #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -3040,10 +3064,12 @@ msgstr "Stakeholder" #: taiga/projects/userstories/api.py:134 msgid "You don't have permissions to set this sprint to this user story." msgstr "" +"No tienes permisos para asignar este sprint a esta historia de usuario." #: taiga/projects/userstories/api.py:138 msgid "You don't have permissions to set this status to this user story." msgstr "" +"No tienes permisos para asignar este estado a esta historia de usuario." #: taiga/projects/userstories/api.py:212 #, python-brace-format diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 3d12be93..d2c0033a 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" "language/fi/)\n" @@ -927,8 +927,8 @@ msgstr "Virheellinen pyynnön tieto" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -948,8 +948,8 @@ msgstr "Virheellinen pyynnön kommentin tieto" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -1039,8 +1039,8 @@ msgstr "Luotu GitLabissa" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 1cdd658c..df5a3036 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" "language/fr/)\n" @@ -848,8 +848,8 @@ msgstr "Information incorrecte sur le problème" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -869,8 +869,8 @@ msgstr "Ignoré" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -945,8 +945,8 @@ msgstr "Créé à partir de GitLab" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index e5f609ad..b37c7ec8 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" "language/nl/)\n" @@ -876,8 +876,8 @@ msgstr "Ongeldige issue informatie" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -897,8 +897,8 @@ msgstr "Ongeldige issue commentaar informatie" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -973,8 +973,8 @@ msgstr "Aangemaakt via GitLab" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 8d8c70c2..b69c9de7 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-05 11:14+0200\n" -"PO-Revision-Date: 2015-07-05 09:14+0000\n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" "taiga-back/language/zh-Hant/)\n" @@ -928,8 +928,8 @@ msgstr "無效的問題資訊" msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\"):\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" "\n" "{description}" msgstr "" @@ -949,8 +949,8 @@ msgstr "無效的議題評論資訊" msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " "@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" -"Origin BitBucket issue: [gh#{number} - {subject}]({bitbucket_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" @@ -1037,8 +1037,8 @@ msgstr "創建立GitLab" msgid "" "Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " "@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" -"Origin GitLab issue: [gh#{number} - {subject}]({gitlab_url} \"Go to " -"'gh#{number} - {subject}'\")\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" msgstr "" From 4df52242594d24eef7e8b61b868ef55eb5e600ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 9 Jul 2015 13:56:28 +0200 Subject: [PATCH 059/190] Divide some long lines --- taiga/timeline/signals.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index b0787946..34a80955 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -21,12 +21,15 @@ from taiga.projects.history import services as history_services from taiga.projects.models import Project from taiga.users.models import User from taiga.projects.history.choices import HistoryType -from taiga.timeline.service import (push_to_timeline, build_user_namespace, - build_project_namespace, extract_user_info) +from taiga.timeline.service import (push_to_timeline, + build_user_namespace, + build_project_namespace, + extract_user_info) # TODO: Add events to followers timeline when followers are implemented. # TODO: Add events to project watchers timeline when project watchers are implemented. + def _push_to_timeline(*args, **kwargs): if settings.CELERY_ENABLED: push_to_timeline.delay(*args, **kwargs) @@ -69,8 +72,6 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d namespace=build_user_namespace(user), extra_data=extra_data) - #Related people: team members - def on_new_history_entry(sender, instance, created, **kwargs): if instance._importing: @@ -125,7 +126,11 @@ def create_membership_push_to_timeline(sender, instance, **kwargs): _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) # If we are updating the old user is removed from project if prev_instance.user: - _push_to_timelines(instance.project, prev_instance.user, prev_instance, "delete", created_datetime) + _push_to_timelines(instance.project, + prev_instance.user, + prev_instance, + "delete", + created_datetime) def delete_membership_push_to_timeline(sender, instance, **kwargs): From 77f9d34be6270be1357c9e0b80b2310d3c934d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 10 Jul 2015 09:58:17 +0200 Subject: [PATCH 060/190] Force settings.DEBUG = False in timeline manage commands --- .../commands/clear_unnecessary_new_membership_entries.py | 8 +++----- taiga/timeline/management/commands/rebuild_timeline.py | 9 +++------ .../commands/rebuild_timeline_for_user_creation.py | 7 ++----- .../commands/update_timeline_for_updated_tasks.py | 9 +++------ 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py b/taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py index f9b65dcf..7790b753 100644 --- a/taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py +++ b/taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py @@ -15,18 +15,16 @@ # along with this program. If not, see . from django.conf import settings from django.core.management.base import BaseCommand +from django.test.utils import override_settings from taiga.timeline.models import Timeline from taiga.projects.models import Project class Command(BaseCommand): help = 'Regenerate unnecessary new memberships entry lines' - def handle(self, *args, **options): - debug_enabled = settings.DEBUG - if debug_enabled: - print("Please, execute this script only with DEBUG mode disabled (DEBUG=False)") - return + @override_settings(DEBUG=False) + def handle(self, *args, **options): removing_timeline_ids = [] for t in Timeline.objects.filter(event_type="projects.membership.create").order_by("created"): print(t.created) diff --git a/taiga/timeline/management/commands/rebuild_timeline.py b/taiga/timeline/management/commands/rebuild_timeline.py index 2c462ce0..5d95d4e4 100644 --- a/taiga/timeline/management/commands/rebuild_timeline.py +++ b/taiga/timeline/management/commands/rebuild_timeline.py @@ -25,6 +25,8 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.management.base import BaseCommand from django.db.models import Model from django.db import reset_queries +from django.test.utils import override_settings + from taiga.projects.models import Project from taiga.projects.history import services as history_services @@ -165,13 +167,8 @@ class Command(BaseCommand): help='Selected project id for timeline generation'), ) - + @override_settings(DEBUG=False) def handle(self, *args, **options): - debug_enabled = settings.DEBUG - if debug_enabled: - print("Please, execute this script only with DEBUG mode disabled (DEBUG=False)") - return - if options["purge"] == True: Timeline.objects.all().delete() diff --git a/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py b/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py index 2982969a..78e637d1 100644 --- a/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py +++ b/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py @@ -23,6 +23,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.core.management.base import BaseCommand from django.db.models import Model from django.db import reset_queries +from django.test.utils import override_settings from taiga.timeline.service import (_get_impl_key_from_model, _timeline_impl_map, extract_user_info) @@ -88,10 +89,6 @@ def generate_timeline(): class Command(BaseCommand): help = 'Regenerate project timeline' + @override_settings(DEBUG=False) def handle(self, *args, **options): - debug_enabled = settings.DEBUG - if debug_enabled: - print("Please, execute this script only with DEBUG mode disabled (DEBUG=False)") - return - generate_timeline() diff --git a/taiga/timeline/management/commands/update_timeline_for_updated_tasks.py b/taiga/timeline/management/commands/update_timeline_for_updated_tasks.py index 15b42172..1d145d30 100644 --- a/taiga/timeline/management/commands/update_timeline_for_updated_tasks.py +++ b/taiga/timeline/management/commands/update_timeline_for_updated_tasks.py @@ -18,6 +18,7 @@ from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist from django.core.management.base import BaseCommand from django.db.models import Prefetch, F +from django.test.utils import override_settings from taiga.timeline.models import Timeline from taiga.timeline.timeline_implementations import userstory_timeline @@ -34,7 +35,7 @@ def update_timeline(initial_date, final_date): timelines = timelines.filter(created__lt=final_date) timelines = timelines.filter(event_type="tasks.task.change") - + print("Generating tasks indexed by id dict") task_ids = timelines.values_list("object_id", flat=True) tasks_per_id = {task.id: task for task in Task.objects.filter(id__in=task_ids).select_related("user_story").iterator()} @@ -77,10 +78,6 @@ class Command(BaseCommand): help='Final date for timeline update'), ) + @override_settings(DEBUG=False) def handle(self, *args, **options): - debug_enabled = settings.DEBUG - if debug_enabled: - print("Please, execute this script only with DEBUG mode disabled (DEBUG=False)") - return - update_timeline(options["initial_date"], options["final_date"]) From b079f7709caa7b451b10eb5c3440ab5de60ca89a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 10 Jul 2015 13:06:32 +0200 Subject: [PATCH 061/190] Minor fixex in local.py.example --- settings/local.py.example | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/settings/local.py.example b/settings/local.py.example index dd4ce8c9..03fa20c6 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -16,6 +16,10 @@ from .development import * +#ADMINS = ( +# ("Admin", "example@example.com"), +#) + DATABASES = { 'default': { 'ENGINE': 'transaction_hooks.backends.postgresql_psycopg2', @@ -27,11 +31,25 @@ DATABASES = { } } -#HOST="http://taiga.projects.kaleidos.net" -# +#SITES = { +# "api": { +# "scheme": "http", +# "domain": "localhost:8000", +# "name": "api" +# }, +# "front": { +# "scheme": "http", +# "domain": "localhost:9001", +# "name": "front" +# }, +#} + +#SITE_ID = "api" + #MEDIA_ROOT = '/home/taiga/media' #STATIC_ROOT = '/home/taiga/static' + # EMAIL SETTINGS EXAMPLE #EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' #EMAIL_USE_TLS = False From 181a3858d1c8a7ee1bfe2305f3c82e4b50e61d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 10 Jul 2015 13:23:33 +0200 Subject: [PATCH 062/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 3 +-- taiga/locale/de/LC_MESSAGES/django.po | 16 +++++++++------- taiga/locale/es/LC_MESSAGES/django.po | 3 +-- taiga/locale/fi/LC_MESSAGES/django.po | 3 +-- taiga/locale/fr/LC_MESSAGES/django.po | 3 +-- taiga/locale/nl/LC_MESSAGES/django.po | 3 +-- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 4 ++-- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 8ed89dce..d1fe3a04 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -12,8 +12,7 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" -"language/ca/)\n" +"Language-Team: Catalan (http://www.transifex.com/p/taiga-back/language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 6fe59434..bc0f2ec2 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,10 +14,9 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-07-06 16:06+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" -"Last-Translator: Taiga Dev Team \n" -"Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" -"language/de/)\n" +"PO-Revision-Date: 2015-07-09 10:40+0000\n" +"Last-Translator: Regina \n" +"Language-Team: German (http://www.transifex.com/p/taiga-back/language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -1345,7 +1344,7 @@ msgstr "Jitsi" #: taiga/projects/choices.py:23 msgid "Custom" -msgstr "" +msgstr "Kunde" #: taiga/projects/choices.py:24 msgid "Talky" @@ -1353,7 +1352,7 @@ msgstr "Gesprächig" #: taiga/projects/custom_attributes/models.py:33 msgid "Text" -msgstr "" +msgstr "Text" #: taiga/projects/custom_attributes/models.py:34 msgid "Multi-Line Text" @@ -1526,7 +1525,7 @@ msgstr "Blockierungsgrund" #: taiga/projects/history/templatetags/functions.py:28 msgid "sprint" -msgstr "" +msgstr "Sprint" #: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." @@ -2688,10 +2687,13 @@ msgstr "Projektende" #: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 msgid "You don't have permissions to set this sprint to this task." msgstr "" +"Sie haben nicht die Berechtigung, diesen Sprint auf diese Aufgabe zu setzen" #: taiga/projects/tasks/api.py:93 msgid "You don't have permissions to set this user story to this task." msgstr "" +"Sie haben nicht die Berechtigung, diese User-Story auf diese Aufgabe zu " +"setzen" #: taiga/projects/tasks/api.py:96 msgid "You don't have permissions to set this status to this task." diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 1a86b943..73bac6ef 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -15,8 +15,7 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:16+0000\n" "Last-Translator: David Barragán \n" -"Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" -"language/es/)\n" +"Language-Team: Spanish (http://www.transifex.com/p/taiga-back/language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index d2c0033a..dbd2a3b3 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -12,8 +12,7 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" -"language/fi/)\n" +"Language-Team: Finnish (http://www.transifex.com/p/taiga-back/language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index df5a3036..cc4ee377 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -18,8 +18,7 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" -"language/fr/)\n" +"Language-Team: French (http://www.transifex.com/p/taiga-back/language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index b37c7ec8..b783b143 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -12,8 +12,7 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" -"language/nl/)\n" +"Language-Team: Dutch (http://www.transifex.com/p/taiga-back/language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index b69c9de7..677e57d3 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" -"taiga-back/language/zh-Hant/)\n" +"Language-Team: Chinese Traditional (http://www.transifex.com/p/taiga-back/" +"language/zh-Hant/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" From c13dcb7757b85cf36c98cda14166c8ebf5a57f76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Jul 2015 17:17:13 +0200 Subject: [PATCH 063/190] New migration to add videoconferences_extra_data field in Project and ProjectTenmplate --- .../migrations/0023_auto_20150721_1511.py | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 taiga/projects/migrations/0023_auto_20150721_1511.py diff --git a/taiga/projects/migrations/0023_auto_20150721_1511.py b/taiga/projects/migrations/0023_auto_20150721_1511.py new file mode 100644 index 00000000..0762e1d0 --- /dev/null +++ b/taiga/projects/migrations/0023_auto_20150721_1511.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0022_auto_20150701_0924'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='videoconferences_extra_data', + field=models.CharField(max_length=250, blank=True, null=True, verbose_name='videoconference extra data'), + preserve_default=True, + ), + migrations.AlterField( + model_name='projecttemplate', + name='videoconferences_extra_data', + field=models.CharField(max_length=250, blank=True, null=True, verbose_name='videoconference extra data'), + preserve_default=True, + ), + ] From 86a8d359b6368e835c7d8d0ca1abe661ed1171b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Jul 2015 12:05:30 +0200 Subject: [PATCH 064/190] Travis: Migrating from legacy to container-based infrastructure --- .travis.yml | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e37362c..d0bb13ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,24 +1,25 @@ +sudo: false language: python python: - "3.4" services: - rabbitmq # will start rabbitmq-server +cache: + - apt + - pip addons: - postgresql: "9.3" + postgresql: "9.4" + apt: + sources: + - deadsnakes + - ubuntu-toolchain-r-test + packages: + - postgresql-plpython-9.4 before_script: - psql -c 'create database taiga;' -U postgres install: - - sudo apt-get update - - sudo apt-get install postgresql-plpython-9.3 - - pip install -r requirements-devel.txt --use-mirrors + - pip install -r requirements-devel.txt script: - - coverage run --source=taiga --omit='*tests*,*commands*,*migrations*,*admin*,*.jinja,*dashboard*,*settings*,*wsgi*,*questions*,*documents*' -m py.test -v --tb=native -notifications: - email: - recipients: - - jespinog@gmail.com - - bameda@dbarragan.com - on_success: change - on_failure: change + - coverage run --source=taiga --omit='*tests*,*commands*,*migrations*,*admin*,*.jinja,*dashboard*,*settings*,*wsgi*,*questions*,*documents*' -m py.test -v --tb=native after_success: - coveralls From 2a4ad10de9a8c503258be1e7e9f6cda1b68b22c7 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 22 Jul 2015 12:12:59 +0200 Subject: [PATCH 065/190] Adding watchers to sample_data --- taiga/projects/management/commands/sample_data.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 3fca91db..22514293 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -286,6 +286,9 @@ class Command(BaseCommand): user__isnull=False)).user bug.save() + watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user + bug.watchers.add(watching_user) + take_snapshot(bug, comment=self.sd.paragraph(), user=bug.owner) @@ -334,6 +337,9 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=task.owner) + watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user + task.watchers.add(watching_user) + # Add history entry task.status=self.sd.db_object_from_queryset(project.task_statuses.all()) task.save() @@ -380,6 +386,9 @@ class Command(BaseCommand): us.assigned_to = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user us.save() + watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user + us.watchers.add(watching_user) + take_snapshot(us, comment=self.sd.paragraph(), user=us.owner) From 9f1f002a549ddf469fcff37a4f8f02f04ac46e58 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 22 Jul 2015 12:32:57 +0200 Subject: [PATCH 066/190] Fixing slug project generation for sample_data --- taiga/projects/management/commands/sample_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 22514293..53fe8fb6 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -422,7 +422,8 @@ class Command(BaseCommand): is_private=self.sd.boolean() anon_permissions = not is_private and list(map(lambda perm: perm[0], ANON_PERMISSIONS)) or [] public_permissions = not is_private and list(map(lambda perm: perm[0], ANON_PERMISSIONS)) or [] - project = Project.objects.create(name='Project Example {0}'.format(counter), + project = Project.objects.create(slug='project-%s'%(counter), + name='Project Example {0}'.format(counter), description='Project example {0} description'.format(counter), owner=random.choice(self.users), is_private=is_private, From 938c9bca75cc0c3d997cc764f6746d380ac027a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 22 Jul 2015 16:43:17 +0200 Subject: [PATCH 067/190] Add a full name to the administrator user --- taiga/users/fixtures/initial_user.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/users/fixtures/initial_user.json b/taiga/users/fixtures/initial_user.json index ed7833c1..1b1e7dd8 100644 --- a/taiga/users/fixtures/initial_user.json +++ b/taiga/users/fixtures/initial_user.json @@ -3,7 +3,7 @@ "model": "users.user", "fields": { "username": "admin", - "full_name": "", + "full_name": "Administrator", "bio": "", "lang": "", "color": "", From a909171eb743aff18163d91857f948a46ac34c9d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 22 Jul 2015 13:25:25 +0200 Subject: [PATCH 068/190] Json export generation in a more efficient way --- taiga/base/api/renderers.py | 32 ++++++++++++++----- .../management/commands/dump_project.py | 3 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/taiga/base/api/renderers.py b/taiga/base/api/renderers.py index 1e2a1cdd..2da7ed4c 100644 --- a/taiga/base/api/renderers.py +++ b/taiga/base/api/renderers.py @@ -80,13 +80,7 @@ class JSONRenderer(BaseRenderer): # See: http://www.ietf.org/rfc/rfc4627.txt # Also: http://lucumr.pocoo.org/2013/7/19/application-mimetypes-and-encodings/ - def render(self, data, accepted_media_type=None, renderer_context=None): - """ - Render `data` into JSON. - """ - if data is None: - return bytes() - + def _get_indent(self, accepted_media_type, renderer_context): # If "indent" is provided in the context, then pretty print the result. # E.g. If we"re being called by the BrowsableAPIRenderer. renderer_context = renderer_context or {} @@ -102,6 +96,17 @@ class JSONRenderer(BaseRenderer): except (ValueError, TypeError): indent = None + return indent + + def render(self, data, accepted_media_type=None, renderer_context=None): + """ + Render `data` into JSON. + """ + if data is None: + return bytes() + + indent = self._get_indent(accepted_media_type, renderer_context) + ret = json.dumps(data, cls=self.encoder_class, indent=indent, ensure_ascii=self.ensure_ascii) @@ -113,6 +118,18 @@ class JSONRenderer(BaseRenderer): return bytes(ret.encode("utf-8")) return ret + def render_to_file(self, data, outputfile, accepted_media_type=None, renderer_context=None): + """ + Render `data` into a file with JSON format. + """ + if data is None: + return bytes() + + indent = self._get_indent(accepted_media_type, renderer_context) + + ret = json.dump(data, outputfile, cls=self.encoder_class, + indent=indent, ensure_ascii=self.ensure_ascii) + class UnicodeJSONRenderer(JSONRenderer): ensure_ascii = False @@ -610,4 +627,3 @@ class MultiPartRenderer(BaseRenderer): def render(self, data, accepted_media_type=None, renderer_context=None): return encode_multipart(self.BOUNDARY, data) - diff --git a/taiga/export_import/management/commands/dump_project.py b/taiga/export_import/management/commands/dump_project.py index b1e58d12..2ea0d7a3 100644 --- a/taiga/export_import/management/commands/dump_project.py +++ b/taiga/export_import/management/commands/dump_project.py @@ -35,4 +35,5 @@ class Command(BaseCommand): raise CommandError('Project "%s" does not exist' % project_slug) data = project_to_dict(project) - print(self.renderer.render(data, renderer_context=self.renderer_context).decode('utf-8')) + with open('%s.json'%(project_slug), 'w') as outfile: + self.renderer.render_to_file(data, outfile, renderer_context=self.renderer_context) From 51fe131c136b999dd811e0695d4bcb921430a0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 9 Jul 2015 14:04:24 +0200 Subject: [PATCH 069/190] Fix timelines generation --- taiga/timeline/signals.py | 86 ++++++++++++++++++------------ tests/integration/test_timeline.py | 31 +++++++++-- 2 files changed, 79 insertions(+), 38 deletions(-) diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 34a80955..dc8b5a2d 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -39,38 +39,44 @@ def _push_to_timeline(*args, **kwargs): def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_data={}): if project is not None: - # Project timeline + # Actions related with a project + + ## Project timeline _push_to_timeline(project, obj, event_type, created_datetime, namespace=build_project_namespace(project), extra_data=extra_data) - # User timeline - _push_to_timeline(user, obj, event_type, created_datetime, - namespace=build_user_namespace(user), - extra_data=extra_data) + ## User profile timelines + ## - Me + related_people = User.objects.filter(id=user.id) - # Calculating related people - related_people = User.objects.none() + ## - Owner + if hasattr(obj, "owner_id") and obj.owner_id: + related_people |= User.objects.filter(id=obj.owner_id) - # Assigned to - if hasattr(obj, "assigned_to") and obj.assigned_to and user != obj.assigned_to: - related_people |= User.objects.filter(id=obj.assigned_to.id) + ## - Assigned to + if hasattr(obj, "assigned_to_id") and obj.assigned_to_id: + related_people |= User.objects.filter(id=obj.assigned_to_id) - # Watchers - watchers = hasattr(obj, "watchers") and obj.watchers.exclude(id=user.id) or User.objects.none() - if watchers: - related_people |= watchers + ## - Watchers + watchers = getattr(obj, "watchers", None) + if watchers: + related_people |= obj.watchers.all() - if project is not None: - # Team - team_members_ids = project.memberships.filter(user__isnull=False).values_list("id", flat=True) - team = User.objects.filter(id__in=team_members_ids) - related_people |= team + ## - Exclude inactive and system users and remove duplicate + related_people = related_people.exclude(is_active=False) + related_people = related_people.exclude(is_system=True) related_people = related_people.distinct() _push_to_timeline(related_people, obj, event_type, created_datetime, namespace=build_user_namespace(user), extra_data=extra_data) + else: + # Actions not related with a project + ## - Me + _push_to_timeline(user, obj, event_type, created_datetime, + namespace=build_user_namespace(user), + extra_data=extra_data) def on_new_history_entry(sender, instance, created, **kwargs): @@ -110,27 +116,37 @@ def on_new_history_entry(sender, instance, created, **kwargs): def create_membership_push_to_timeline(sender, instance, **kwargs): - # Creating new membership with associated user - # If the user is the project owner we don't do anything because that info will - # be shown in created project timeline entry + """ + Creating new membership with associated user. If the user is the project owner we don't + do anything because that info will be shown in created project timeline entry + + @param sender: Membership model + @param instance: Membership object + """ + + # We shown in created project timeline entry if not instance.pk and instance.user and instance.user != instance.project.owner: created_datetime = instance.created_at _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) - #Updating existing membership + # Updating existing membership elif instance.pk: - prev_instance = sender.objects.get(pk=instance.pk) - if instance.user != prev_instance.user: - created_datetime = timezone.now() - # The new member - _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) - # If we are updating the old user is removed from project - if prev_instance.user: - _push_to_timelines(instance.project, - prev_instance.user, - prev_instance, - "delete", - created_datetime) + try: + prev_instance = sender.objects.get(pk=instance.pk) + if instance.user != prev_instance.user: + created_datetime = timezone.now() + # The new member + _push_to_timelines(instance.project, instance.user, instance, "create", created_datetime) + # If we are updating the old user is removed from project + if prev_instance.user: + _push_to_timelines(instance.project, + prev_instance.user, + prev_instance, + "delete", + created_datetime) + except sender.DoesNotExist: + # This happens with some tests, when a membership is created with a concrete id + pass def delete_membership_push_to_timeline(sender, instance, **kwargs): diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 3d203494..957c5c91 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -377,7 +377,7 @@ def test_owner_user_story_timeline(): def test_assigned_to_user_story_timeline(): membership = factories.MembershipFactory.create() - user_story = factories.UserStoryFactory.create(subject="test us timeline", assigned_to=membership.user) + user_story = factories.UserStoryFactory.create(subject="test us timeline", assigned_to=membership.user, project=membership.project) history_services.take_snapshot(user_story, user=user_story.owner) user_timeline = service.get_profile_timeline(user_story.assigned_to) assert user_timeline[0].event_type == "userstories.userstory.create" @@ -386,20 +386,22 @@ def test_assigned_to_user_story_timeline(): def test_watchers_to_user_story_timeline(): membership = factories.MembershipFactory.create() - user_story = factories.UserStoryFactory.create(subject="test us timeline") + user_story = factories.UserStoryFactory.create(subject="test us timeline", project=membership.project) user_story.watchers.add(membership.user) history_services.take_snapshot(user_story, user=user_story.owner) user_timeline = service.get_profile_timeline(membership.user) assert user_timeline[0].event_type == "userstories.userstory.create" assert user_timeline[0].data["userstory"]["subject"] == "test us timeline" -def test_user_data_for_system_users(): + +def test_user_data_for_non_system_users(): user_story = factories.UserStoryFactory.create(subject="test us timeline") history_services.take_snapshot(user_story, user=user_story.owner) project_timeline = service.get_project_timeline(user_story.project) serialized_obj = TimelineSerializer(project_timeline[0]) serialized_obj.data["data"]["user"]["is_profile_visible"] = True + def test_user_data_for_system_users(): user_story = factories.UserStoryFactory.create(subject="test us timeline") user_story.owner.is_system = True @@ -409,6 +411,7 @@ def test_user_data_for_system_users(): serialized_obj = TimelineSerializer(project_timeline[0]) serialized_obj.data["data"]["user"]["is_profile_visible"] = False + def test_user_data_for_unactived_users(): user_story = factories.UserStoryFactory.create(subject="test us timeline") user_story.owner.cancel() @@ -418,3 +421,25 @@ def test_user_data_for_unactived_users(): serialized_obj = TimelineSerializer(project_timeline[0]) serialized_obj.data["data"]["user"]["is_profile_visible"] = False serialized_obj.data["data"]["user"]["username"] = "deleted-user" + +def test_timeline_error_use_member_ids_instead_of_memberships_ids(): + user_story = factories.UserStoryFactory.create(subject="test error use member ids instead of " + "memberships ids") + + member_user = user_story.owner + external_user = factories.UserFactory.create() + + membership = factories.MembershipFactory.create(project=user_story.project, + user=member_user, + id=external_user.id) + + history_services.take_snapshot(user_story, user=member_user) + + user_timeline = service.get_profile_timeline(member_user) + assert len(user_timeline) == 2 + assert user_timeline[0].event_type == "userstories.userstory.create" + assert user_timeline[1].event_type == "users.user.create" + + external_user_timeline = service.get_profile_timeline(external_user) + assert len(external_user_timeline) == 1 + assert external_user_timeline[0].event_type == "users.user.create" From a63182e34204112a77eaec54364bf853e58df5d5 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 21 Jul 2015 09:25:20 +0200 Subject: [PATCH 070/190] Adding command for rebuilding timelines iterating per project --- ...rebuild_timeline_iterating_per_projects.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py diff --git a/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py b/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py new file mode 100644 index 00000000..527a85bf --- /dev/null +++ b/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py @@ -0,0 +1,35 @@ +# 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 django.core.management.base import BaseCommand +from django.test.utils import override_settings +from django.core.management import call_command + +from taiga.projects.models import Project + + +class Command(BaseCommand): + help = 'Regenerate projects timeline iterating per project' + + @override_settings(DEBUG=False) + def handle(self, *args, **options): + total = Project.objects.count() + + for count,project in enumerate(Project.objects.order_by("id")): + print("""*********************************** + %s/%s %s +***********************************"""%(count+1, total, project.name)) + call_command("rebuild_timeline", project=project.id) From 7fdbe2907abe30a953242243a826f459ae2a0a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Jul 2015 17:13:03 +0200 Subject: [PATCH 071/190] Disabled some unnecessary timeline commands --- ...ip_entries.py => _clear_unnecessary_new_membership_entries.py} | 0 ...or_user_creation.py => _rebuild_timeline_for_user_creation.py} | 0 ...for_updated_tasks.py => _update_timeline_for_updated_tasks.py} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename taiga/timeline/management/commands/{clear_unnecessary_new_membership_entries.py => _clear_unnecessary_new_membership_entries.py} (100%) rename taiga/timeline/management/commands/{rebuild_timeline_for_user_creation.py => _rebuild_timeline_for_user_creation.py} (100%) rename taiga/timeline/management/commands/{update_timeline_for_updated_tasks.py => _update_timeline_for_updated_tasks.py} (100%) diff --git a/taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py b/taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py similarity index 100% rename from taiga/timeline/management/commands/clear_unnecessary_new_membership_entries.py rename to taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py diff --git a/taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py b/taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py similarity index 100% rename from taiga/timeline/management/commands/rebuild_timeline_for_user_creation.py rename to taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py diff --git a/taiga/timeline/management/commands/update_timeline_for_updated_tasks.py b/taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py similarity index 100% rename from taiga/timeline/management/commands/update_timeline_for_updated_tasks.py rename to taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py From 7ad293b311dcb77e46b65f9c2bac3392712afee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 21 Jul 2015 20:05:05 +0200 Subject: [PATCH 072/190] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d22da965..a5cd0e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. - Add custom videoconference system. - Add support for comments in the Gitlab webhooks integration. +- Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer From 1ea444fd38f403481b50927ac2307cdaeb8919bc Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 24 Jul 2015 13:56:30 +0200 Subject: [PATCH 073/190] Enabling kanban module for al the sample_data --- taiga/projects/management/commands/sample_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 53fe8fb6..0a5e583e 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -431,7 +431,8 @@ class Command(BaseCommand): public_permissions=public_permissions, total_story_points=self.sd.int(600, 3000), total_milestones=self.sd.int(5,10)) - + project.is_kanban_activated = True + project.save() take_snapshot(project, user=project.owner) return project From cd8e3160aa12a7196fe55a109ec1d8dd5f16d973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sun, 26 Jul 2015 23:31:12 +0200 Subject: [PATCH 074/190] [i18n] Add polish (pl) translation --- CHANGELOG.md | 2 + settings/common.py | 2 +- taiga/locale/pl/LC_MESSAGES/django.po | 3570 +++++++++++++++++++++++++ 3 files changed, 3573 insertions(+), 1 deletion(-) create mode 100644 taiga/locale/pl/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index a5cd0e5f..6049676a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - Add custom videoconference system. - Add support for comments in the Gitlab webhooks integration. - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. +- i18n. + - Add polish (pl) translation. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer diff --git a/settings/common.py b/settings/common.py index 716760aa..69048eba 100644 --- a/settings/common.py +++ b/settings/common.py @@ -126,7 +126,7 @@ LANGUAGES = [ #("nn", "Norsk (nynorsk)"), # Norwegian Nynorsk #("os", "Ирон æвзаг"), # Ossetic #("pa", "ਪੰਜਾਬੀ"), # Punjabi - #("pl", "Polski"), # Polish + ("pl", "Polski"), # Polish #("pt", "Português (Portugal)"), # Portuguese #("pt-br", "Português (Brasil)"), # Brazilian Portuguese #("ro", "Română"), # Romanian diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po new file mode 100644 index 00000000..54f55ac7 --- /dev/null +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -0,0 +1,3570 @@ +# taiga-back.taiga. +# Copyright (C) 2015 Taiga Dev Team +# This file is distributed under the same license as the taiga-back package. +# +# Translators: +# Wojtek Jurkowlaniec , 2015 +msgid "" +msgstr "" +"Project-Id-Version: taiga-back\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"PO-Revision-Date: 2015-07-26 13:15+0000\n" +"Last-Translator: Konrad Krawczuk \n" +"Language-Team: Polish (http://www.transifex.com/projects/p/taiga-back/" +"language/pl/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pl\n" +"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 " +"|| n%100>=20) ? 1 : 2);\n" + +#: taiga/auth/api.py:99 +msgid "Public register is disabled." +msgstr "Publiczna rejestracja jest wyłączona" + +#: taiga/auth/api.py:132 +msgid "invalid register type" +msgstr "Nieprawidłowy typ rejestracji" + +#: taiga/auth/api.py:145 +msgid "invalid login type" +msgstr "Nieprawidłowy typ logowania" + +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +msgid "invalid username" +msgstr "Nieprawidłowa nazwa użytkownika" + +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +msgid "" +"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" +msgstr "Wymagane. Maksymalnie 255 znaków. Litery, cyfry oraz /./-/_ " + +#: taiga/auth/services.py:75 +msgid "Username is already in use." +msgstr "Nazwa użytkownika jest już używana." + +#: taiga/auth/services.py:78 +msgid "Email is already in use." +msgstr "Ten adres email jest już w użyciu" + +#: taiga/auth/services.py:94 +msgid "Token not matches any valid invitation." +msgstr "Token nie zgadza się z żadnym zaproszeniem" + +#: taiga/auth/services.py:122 +msgid "User is already registered." +msgstr "Użytkownik już zarejestrowany" + +#: taiga/auth/services.py:146 +msgid "Membership with user is already exists." +msgstr "Członkowstwo z użytkownikiem już istnieje." + +#: taiga/auth/services.py:172 +msgid "Error on creating new user." +msgstr "Błąd przy tworzeniu użytkownika." + +#: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +msgid "Invalid token" +msgstr "Nieprawidłowy token" + +#: taiga/base/api/fields.py:268 +msgid "This field is required." +msgstr "To pole jest wymagane." + +#: taiga/base/api/fields.py:269 taiga/base/api/relations.py:311 +msgid "Invalid value." +msgstr "Nieprawidłowa wartość." + +#: taiga/base/api/fields.py:453 +#, python-format +msgid "'%s' value must be either True or False." +msgstr "'%s' wartość musi przyjąć True albo False," + +#: taiga/base/api/fields.py:517 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Podaj prawidłowy 'slug' zawierający litery, cyfry, podkreślenia lub myślniki." + +#: taiga/base/api/fields.py:532 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Dokonał właściwego wyboru. Wartość %(value)s nie jest jedną z dostępnych " +"opcji." + +#: taiga/base/api/fields.py:595 +msgid "Enter a valid email address." +msgstr "Podaj właściwy adres email." + +#: taiga/base/api/fields.py:637 +#, python-format +msgid "Date has wrong format. Use one of these formats instead: %s" +msgstr "Zły format. Użyj jednego z tych formatów: %s" + +#: taiga/base/api/fields.py:701 +#, python-format +msgid "Datetime has wrong format. Use one of these formats instead: %s" +msgstr "Zły format. Użyj jednego z tych formatów: %s" + +#: taiga/base/api/fields.py:771 +#, python-format +msgid "Time has wrong format. Use one of these formats instead: %s" +msgstr "Zły format. Użyj jednego z tych formatów: %s" + +#: taiga/base/api/fields.py:828 +msgid "Enter a whole number." +msgstr "Wpisz cały numer" + +#: taiga/base/api/fields.py:829 taiga/base/api/fields.py:882 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Upewnij się, że wartość jest mniejsza lub równa od %(limit_value)s." + +#: taiga/base/api/fields.py:830 taiga/base/api/fields.py:883 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Upewnij się, że wartość jest większa lub równa od %(limit_value)s." + +#: taiga/base/api/fields.py:860 +#, python-format +msgid "\"%s\" value must be a float." +msgstr "\"%s\" wartość musi być zmiennoprzecinkowa." + +#: taiga/base/api/fields.py:881 +msgid "Enter a number." +msgstr "Wpisz numer." + +#: taiga/base/api/fields.py:884 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "Upewnij się że nie podałeś więcej niż %s znaków." + +#: taiga/base/api/fields.py:885 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "Upewnij się, że nie ma więcej niż %s miejsc po przecinku." + +#: taiga/base/api/fields.py:886 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "Upewnij się, że nie ma więcej niż %s cyfr przed przecinkiem." + +#: taiga/base/api/fields.py:953 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Plik nie został wysłany. Sprawdź kodowanie znaków w formularzu." + +#: taiga/base/api/fields.py:954 +msgid "No file was submitted." +msgstr "Plik nie został wysłany." + +#: taiga/base/api/fields.py:955 +msgid "The submitted file is empty." +msgstr "Wysłany plik jest pusty." + +#: taiga/base/api/fields.py:956 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "" +"Upewnij się, że nazwa pliku ma maksymalnie %(max)d znaków.(Ilość znaków to: " +"%(length)d)." + +#: taiga/base/api/fields.py:957 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Proszę wybrać jedną z opcji, nie obie." + +#: taiga/base/api/fields.py:997 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Prześlij właściwy obraz. Plik który próbujesz przesłać nie jest obrazem lub " +"jest uszkodzony." + +#: taiga/base/api/pagination.py:115 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "Strona nie jest ostatnią i nie może zostać zmieniona na int." + +#: taiga/base/api/pagination.py:119 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Niewłaściwa strona (%(page_number)s): %(message)s" + +#: taiga/base/api/permissions.py:61 +msgid "Invalid permission definition." +msgstr "Nieprawidłowa definicja uprawnień." + +#: taiga/base/api/relations.py:221 +#, python-format +msgid "Invalid pk '%s' - object does not exist." +msgstr "Nieprawidłowa wartość klucza '%s' -Obiekt nie istniej." + +#: taiga/base/api/relations.py:222 +#, python-format +msgid "Incorrect type. Expected pk value, received %s." +msgstr "Niepoprawny typ. Oczekiwana wartość, otrzymana %s." + +#: taiga/base/api/relations.py:310 +#, python-format +msgid "Object with %s=%s does not exist." +msgstr "Obiekt z %s=%s nie istnieje." + +#: taiga/base/api/relations.py:346 +msgid "Invalid hyperlink - No URL match" +msgstr "Nieprawidłowy odnośnik - brak pasującego URL" + +#: taiga/base/api/relations.py:347 +msgid "Invalid hyperlink - Incorrect URL match" +msgstr "Nieprawidłowy odnośnik - źle dopasowany URL" + +#: taiga/base/api/relations.py:348 +msgid "Invalid hyperlink due to configuration error" +msgstr "Nieprawidłowy odnośnik z powodu błędu konfiguracji" + +#: taiga/base/api/relations.py:349 +msgid "Invalid hyperlink - object does not exist." +msgstr "Nieprawidłowy odnośnik - obiekt nie istnieje." + +#: taiga/base/api/relations.py:350 +#, python-format +msgid "Incorrect type. Expected url string, received %s." +msgstr "Niepoprawny typ. Oczekiwany url, otrzymany %s." + +#: taiga/base/api/serializers.py:296 +msgid "Invalid data" +msgstr "Nieprawidłowa dana" + +#: taiga/base/api/serializers.py:388 +msgid "No input provided" +msgstr "Nic nie wpisano" + +#: taiga/base/api/serializers.py:548 +msgid "Cannot create a new item, only existing items may be updated." +msgstr "" +"Nie można utworzyć nowego obiektu, tylko istniejące obiekty mogą być " +"aktualizowane." + +#: taiga/base/api/serializers.py:559 +msgid "Expected a list of items." +msgstr "Oczekiwana lista elementów." + +#: taiga/base/api/views.py:100 +msgid "Not found" +msgstr "Nie znaleziono" + +#: taiga/base/api/views.py:103 +msgid "Permission denied" +msgstr "Dostęp zabroniony" + +#: taiga/base/api/views.py:451 +msgid "Server application error" +msgstr "Błąd aplikacji serwera" + +#: taiga/base/connectors/exceptions.py:24 +msgid "Connection error." +msgstr "Błąd połączenia." + +#: taiga/base/exceptions.py:53 +msgid "Malformed request." +msgstr "Błędne żądanie." + +#: taiga/base/exceptions.py:58 +msgid "Incorrect authentication credentials." +msgstr "Nieprawidłowe dane uwierzytelniające." + +#: taiga/base/exceptions.py:63 +msgid "Authentication credentials were not provided." +msgstr "Nie podano danych uwierzytelniających." + +#: taiga/base/exceptions.py:68 +msgid "You do not have permission to perform this action." +msgstr "Nie masz uprawnień do wykonania tej czynności." + +#: taiga/base/exceptions.py:73 +#, python-format +msgid "Method '%s' not allowed." +msgstr "Metoda %s nie dozwolona." + +#: taiga/base/exceptions.py:81 +msgid "Could not satisfy the request's Accept header" +msgstr "Nie udało się spełnić żądania Accept Header" + +#: taiga/base/exceptions.py:90 +#, python-format +msgid "Unsupported media type '%s' in request." +msgstr "Niewspierany typ pliku '%s' w żądaniu." + +#: taiga/base/exceptions.py:98 +msgid "Request was throttled." +msgstr "Żądanie zostało zduszone." + +#: taiga/base/exceptions.py:99 +#, python-format +msgid "Expected available in %d second%s." +msgstr "Oczekiwana dostępność w ciągu %d sekund%s." + +#: taiga/base/exceptions.py:113 +msgid "Unexpected error" +msgstr "Nieoczekiwany błąd" + +#: taiga/base/exceptions.py:125 +msgid "Not found." +msgstr "Nie odnaleziono." + +#: taiga/base/exceptions.py:130 +msgid "Method not supported for this endpoint." +msgstr "Metoda nie wspierana dla tej końcówki." + +#: taiga/base/exceptions.py:138 taiga/base/exceptions.py:146 +msgid "Wrong arguments." +msgstr "Złe argumenty." + +#: taiga/base/exceptions.py:150 +msgid "Data validation error" +msgstr "Błąd walidacji dancyh" + +#: taiga/base/exceptions.py:162 +msgid "Integrity Error for wrong or invalid arguments" +msgstr "Błąd integralności dla błędnych lub nieprawidłowych argumentów" + +#: taiga/base/exceptions.py:169 +msgid "Precondition error" +msgstr "Błąd warunków wstępnych" + +#: taiga/base/filters.py:74 +msgid "Error in filter params types." +msgstr "Błąd w parametrach typów filtrów." + +#: taiga/base/filters.py:121 taiga/base/filters.py:210 +#: taiga/base/filters.py:259 +msgid "'project' must be an integer value." +msgstr "'project' musi być wartością typu int." + +#: taiga/base/tags.py:25 +msgid "tags" +msgstr "tagi" + +#: taiga/base/templates/emails/base-body-html.jinja:6 +msgid "Taiga" +msgstr "Taiga" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Follow us on Twitter" +msgstr "Obserwuj nas na Twitterze" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Twitter" +msgstr "Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "Get the code on GitHub" +msgstr "Pobierz kog z GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "GitHub" +msgstr "GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Visit our website" +msgstr "Odwiedź naszą stronę www" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Taiga.io" +msgstr "Taiga.io" + +#: taiga/base/templates/emails/base-body-html.jinja:423 +#: taiga/base/templates/emails/hero-body-html.jinja:397 +#: taiga/base/templates/emails/updates-body-html.jinja:459 +#, python-format +msgid "" +"\n" +" Taiga Support:\n" +" %(support_url)s\n" +"
\n" +" Contact us:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Mailing list:\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " +msgstr "" +"\n" +" Pomoc Taiga:\n" +" %(support_url)s\n" +"
\n" +" Skontaktuj się z " +"nami:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Lista mailingowa:" +"\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " + +#: taiga/base/templates/emails/hero-body-html.jinja:6 +msgid "You have been Taigatized" +msgstr "Zostałeś zaTaigowany :)" + +#: taiga/base/templates/emails/hero-body-html.jinja:359 +msgid "" +"\n" +"

You have been Taigatized!" +"

\n" +"

Welcome to Taiga, an Open " +"Source, Agile Project Management Tool

\n" +" " +msgstr "" +"\n" +"

Zostałeś zataigowany!\n" +"

Witaj w Taiga, " +"otwartoźródłowym, zwinnym narzędziu do zarządzania

\n" +" " + +#: taiga/base/templates/emails/updates-body-html.jinja:6 +msgid "[Taiga] Updates" +msgstr "[Taiga] Aktualizacje" + +#: taiga/base/templates/emails/updates-body-html.jinja:417 +msgid "Updates" +msgstr "Aktualizacje" + +#: taiga/base/templates/emails/updates-body-html.jinja:423 +#, python-format +msgid "" +"\n" +"

comment:" +"

\n" +"

" +"%(comment)s

\n" +" " +msgstr "" +"\n" +"

komentarz:" +"

\n" +"

" +"%(comment)s

\n" +" " + +#: taiga/base/templates/emails/updates-body-text.jinja:6 +#, python-format +msgid "" +"\n" +" Comment: %(comment)s\n" +" " +msgstr "" +"\n" +" Komentarz: %(comment)s\n" +" " + +#: taiga/export_import/api.py:103 +msgid "We needed at least one role" +msgstr "Potrzeba conajmiej jednej roli" + +#: taiga/export_import/api.py:197 +msgid "Needed dump file" +msgstr "Wymagany plik zrzutu" + +#: taiga/export_import/api.py:204 +msgid "Invalid dump format" +msgstr "Nieprawidłowy format zrzutu" + +#: taiga/export_import/dump_service.py:96 +msgid "error importing project data" +msgstr "błąd w trakcie importu danych projektu" + +#: taiga/export_import/dump_service.py:109 +msgid "error importing lists of project attributes" +msgstr "błąd w trakcie importu atrybutów projektu" + +#: taiga/export_import/dump_service.py:114 +msgid "error importing default project attributes values" +msgstr "błąd w trakcie importu domyślnych atrybutów projektu" + +#: taiga/export_import/dump_service.py:124 +msgid "error importing custom attributes" +msgstr "błąd w trakcie importu niestandardowych atrybutów" + +#: taiga/export_import/dump_service.py:129 +msgid "error importing roles" +msgstr "błąd w trakcie importu ról" + +#: taiga/export_import/dump_service.py:144 +msgid "error importing memberships" +msgstr "błąd w trakcie importu członkostw" + +#: taiga/export_import/dump_service.py:149 +msgid "error importing sprints" +msgstr "błąd w trakcie importu sprintów" + +#: taiga/export_import/dump_service.py:154 +msgid "error importing wiki pages" +msgstr "błąd w trakcie importu stron Wiki" + +#: taiga/export_import/dump_service.py:159 +msgid "error importing wiki links" +msgstr "błąd w trakcie importu linków Wiki" + +#: taiga/export_import/dump_service.py:164 +msgid "error importing issues" +msgstr "błąd w trakcie importu zgłoszeń" + +#: taiga/export_import/dump_service.py:169 +msgid "error importing user stories" +msgstr "błąd w trakcie importu historyjek użytkownika" + +#: taiga/export_import/dump_service.py:174 +msgid "error importing tasks" +msgstr "błąd w trakcie importu zadań" + +#: taiga/export_import/dump_service.py:179 +msgid "error importing tags" +msgstr "błąd w trakcie importu tagów" + +#: taiga/export_import/dump_service.py:183 +msgid "error importing timelines" +msgstr "błąd w trakcie importu osi czasu" + +#: taiga/export_import/serializers.py:161 +msgid "{}=\"{}\" not found in this project" +msgstr "{}=\"{}\" nie odnaleziono w projekcie" + +#: taiga/export_import/serializers.py:384 +#: taiga/projects/custom_attributes/serializers.py:103 +msgid "Invalid content. It must be {\"key\": \"value\",...}" +msgstr "Niewłaściwa zawartość. Musi to być {\"key\": \"value\",...}" + +#: taiga/export_import/serializers.py:399 +#: taiga/projects/custom_attributes/serializers.py:118 +msgid "It contain invalid custom fields." +msgstr "Zawiera niewłaściwe pola niestandardowe." + +#: taiga/export_import/serializers.py:468 +#: taiga/projects/milestones/serializers.py:63 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +msgid "Name duplicated for the project" +msgstr "Nazwa projektu zduplikowana" + +#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +msgid "Error generating project dump" +msgstr "Błąd w trakcie generowania zrzutu projektu" + +#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +msgid "Error loading project dump" +msgstr "Błąd w trakcie wczytywania zrzutu projektu" + +#: taiga/export_import/templates/emails/dump_project-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump generated

\n" +"

Hello %(user)s,

\n" +"

Your dump from project %(project)s has been correctly generated.\n" +"

You can download it here:

\n" +" Download the dump file\n" +"

This file will be deleted on %(deletion_date)s.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zrzut projektu wygenerowany

\n" +"

Witaj %(user)s,

\n" +"

Twój zrzut projektu %(project)s został wygenerowany prawidłowo.\n" +"

Możesz go pobrać tutaj:

\n" +" Pobierz plik zrzutu\n" +"

Ten plik zostanie usunięty dnia %(deletion_date)s.

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your dump from project %(project)s has been correctly generated. You can " +"download it here:\n" +"\n" +"%(url)s\n" +"\n" +"This file will be deleted on %(deletion_date)s.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(user)s,\n" +"\n" +"Twój zrzut z projektu %(project)s został wygenerowany prawidłowo. Możesz " +"pobrać go tutaj:\n" +"\n" +"%(url)s\n" +"\n" +"Plik zostanie usunięty dnia: %(deletion_date)s.\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/export_import/templates/emails/dump_project-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been generated" +msgstr "[%(project)s] Twój plik zrzutu został wygenerowany" + +#: taiga/export_import/templates/emails/export_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project %(project)s has not been exported correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Witaj %(user)s,

\n" +"

Twój projekt %(project)s Nie został wyeksportowany prawidłowo.

\n" +"

Administrator Taiga został o tym poinformowany.
Proszę spróbuj " +"ponownie lub skontaktuj się z administratorem lub wsparciem Taiga\n" +" %(support_email)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/export_import/templates/emails/export_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"Your project %(project)s has not been exported correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(user)s,\n" +"\n" +"%(error_message)s\n" +"Twój projekt%(project)s nie został wyeksportowany prawidłowo.\n" +"\n" +"Administrator Taiga został o tym poinformowany.\n" +"\n" +"Proszę spróbuj ponownie lub skontaktuj się z administratorem lub wsparciem " +"Taiga %(support_email)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/export_import/templates/emails/export_error-subject.jinja:1 +#, python-format +msgid "[%(project)s] %(error_subject)s" +msgstr "[%(project)s] %(error_subject)s" + +#: taiga/export_import/templates/emails/import_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project has not been importer correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Witaj %(user)s,

\n" +"

Twój projekt nie został zaimportowany prawidłowo.

\n" +"

Administrator Taiga został o tym poinformowany.
Proszę spróbuj " +"ponownie lub skontaktuj się z administratorem lub wsparciem Taiga\n" +" %(support_email)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/export_import/templates/emails/import_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Your project has not been importer correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Twój projekt nie został zaimportowany prawidłowo.\n" +"\n" +"Administrator Taiga został o tym poinformowany.\n" +"\n" +"roszę spróbuj ponownie lub skontaktuj się z administratorem lub wsparciem " +"Taiga %(support_email)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/export_import/templates/emails/import_error-subject.jinja:1 +#, python-format +msgid "[Taiga] %(error_subject)s" +msgstr "[Taiga] %(error_subject)s" + +#: taiga/export_import/templates/emails/load_dump-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump imported

\n" +"

Hello %(user)s,

\n" +"

Your project dump has been correctly imported.

\n" +" Go to %(project)s\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zrzut projektu zaimportowany

\n" +"

Witaj %(user)s,

\n" +"

Twój zrzut projektu został prawidłowo zaimportowany.

\n" +" Idź do %(project)s\n" +"

Zespół Taiga

\n" +" " + +#: taiga/export_import/templates/emails/load_dump-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your project dump has been correctly imported.\n" +"\n" +"You can see the project %(project)s here:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(user)s,\n" +"\n" +"Twój zrzut projektu został prawidłowo zaimportowany.\n" +"\n" +"Możesz zobaczyć projekt %(project)s tutaj:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/export_import/templates/emails/load_dump-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been imported" +msgstr "[%(project)s] Twój zrzut projektu został prawidłowo zaimportowany" + +#: taiga/feedback/models.py:23 taiga/users/models.py:111 +msgid "full name" +msgstr "Imię i Nazwisko" + +#: taiga/feedback/models.py:25 taiga/users/models.py:106 +msgid "email address" +msgstr "adres e-mail" + +#: taiga/feedback/models.py:27 +msgid "comment" +msgstr "komentarz" + +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 +#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 +#: taiga/projects/models.py:129 taiga/projects/models.py:561 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +msgid "created date" +msgstr "data utworzenia" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Feedback

\n" +"

Taiga has received feedback from %(full_name)s <%(email)s>

\n" +" " +msgstr "" +"\n" +"

Feedback

\n" +"

Taiga otrzymała informacje od %(full_name)s <%(email)s>

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:9 +#, python-format +msgid "" +"\n" +"

Comment

\n" +"

%(comment)s

\n" +" " +msgstr "" +"\n" +"

Komentarz

\n" +"

%(comment)s

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 +#: taiga/users/admin.py:51 +msgid "Extra info" +msgstr "Dodatkowe info" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:1 +#, python-format +msgid "" +"---------\n" +"- From: %(full_name)s <%(email)s>\n" +"---------\n" +"- Comment:\n" +"%(comment)s\n" +"---------" +msgstr "" +"---------\n" +"- Od: %(full_name)s <%(email)s>\n" +"---------\n" +"- Komentarz:\n" +"%(comment)s\n" +"---------" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:8 +msgid "- Extra info:" +msgstr "- Dodatkowe info:" + +#: taiga/feedback/templates/emails/feedback_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Feedback from %(full_name)s <%(email)s>\n" +msgstr "" +"\n" +"[Taiga] Informacje od %(full_name)s <%(email)s>\n" + +#: taiga/hooks/api.py:52 +msgid "The payload is not a valid json" +msgstr "Źródło nie jest prawidłowym plikiem json" + +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 +#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +msgid "The project doesn't exist" +msgstr "Projekt nie istnieje" + +#: taiga/hooks/api.py:64 +msgid "Bad signature" +msgstr "Błędna sygnatura" + +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +msgid "The referenced element doesn't exist" +msgstr "Element referencyjny nie istnieje" + +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +msgid "The status doesn't exist" +msgstr "Status nie istnieje" + +#: taiga/hooks/bitbucket/event_hooks.py:86 +msgid "Status changed from BitBucket commit" +msgstr "Status zmieniony przez commit z BitBucket" + +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Nieprawidłowa informacja o zgłoszeniu" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Zgłoszenie utworzone przez [@{bitbucket_user_name}]({bitbucket_user_url} " +"\"Zobacz profil użytkownika @{bitbucket_user_name}'s \") na BitBucket.\n" +"Źródłowe zgłoszenie z BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Idź do 'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "Zgłoszenie utworzone przez BitBucket." + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Nieprawidłowa informacja o komentarzu do zgłoszenia" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Skomentowane przez [@{bitbucket_user_name}]({bitbucket_user_url} \"Zobacz " +"profil użytkownika @{bitbucket_user_name}'s\") na BitBucket.\n" +"Źródłowe zgłoszenie z BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Idź do 'bb#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" +"Komentarz z BitBucket:\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:96 +#, python-brace-format +msgid "" +"Status changed by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." +msgstr "" +"Status zmieniony przez [@{github_user_name}]({github_user_url} \"Zobacz " +"profil użytkownika @{github_user_name}'s \") z commitu na GitHub " +"[{commit_id}]({commit_url} \"Zobacz commit'{commit_id} - " +"{commit_message}'\")." + +#: taiga/hooks/github/event_hooks.py:107 +msgid "Status changed from GitHub commit." +msgstr "Status zmieniony przez commit z GitHub" + +#: taiga/hooks/github/event_hooks.py:157 +#, python-brace-format +msgid "" +"Issue created by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Zgłoszenie utworzone przez [@{github_user_name}]({github_user_url} \"Zobacz " +"profil użytkownika @{github_user_name}'s \") na GitHub.\n" +"Źródłowe zgłoszenie z GitHub: [gh#{number} - {subject}]({github_url} \"Idź " +"do 'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/github/event_hooks.py:168 +msgid "Issue created from GitHub." +msgstr "Zgłoszenie utworzone przez GitHub." + +#: taiga/hooks/github/event_hooks.py:200 +#, python-brace-format +msgid "" +"Comment by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Skomentowane przez [@{github_user_name}]({github_user_url} \"Zobacz profil " +"użytkownika @{github_user_name}'s GitHub profile\") na GitHub.\n" +"Źródłowe zgłoszenie z GitHub: [gh#{number} - {subject}]({github_url} \"Idź " +"do 'gh#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:211 +#, python-brace-format +msgid "" +"Comment From GitHub:\n" +"\n" +"{message}" +msgstr "" +"Komentarz z GitHub:\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:86 +msgid "Status changed from GitLab commit" +msgstr "Status zmieniony przez commit z GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:128 +msgid "Created from GitLab" +msgstr "Utworzone przez GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Skomentowane przez [@{gitlab_user_name}]({gitlab_user_url} \"Zobacz profil " +"użytkownika @{gitlab_user_name}'s \") na GitLab.\n" +"Źródłowe zgłoszenie z: [gl#{number} - {subject}]({gitlab_url} \"Idź do " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" +"Komentarz z GitLab:\n" +"\n" +"{message}" + +#: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 +#: taiga/permissions/permissions.py:52 +msgid "View project" +msgstr "Zobacz projekt" + +#: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 +#: taiga/permissions/permissions.py:54 +msgid "View milestones" +msgstr "Zobacz kamienie milowe" + +#: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 +msgid "View user stories" +msgstr "Zobacz historyjki użytkownika" + +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:64 +msgid "View tasks" +msgstr "Zobacz zadania" + +#: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 +#: taiga/permissions/permissions.py:69 +msgid "View issues" +msgstr "Zobacz zgłoszenia" + +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:75 +msgid "View wiki pages" +msgstr "Zobacz strony Wiki" + +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 +#: taiga/permissions/permissions.py:80 +msgid "View wiki links" +msgstr "Zobacz linki Wiki" + +#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 +msgid "Vote issues" +msgstr "Głosuj na zgłoszenia" + +#: taiga/permissions/permissions.py:39 +msgid "Request membership" +msgstr "Poproś o członkowstwo" + +#: taiga/permissions/permissions.py:40 +msgid "Add user story to project" +msgstr "Dodaj historyjkę użytkownika do projektu" + +#: taiga/permissions/permissions.py:41 +msgid "Add comments to user stories" +msgstr "Dodaj komentarze do historyjek użytkownika" + +#: taiga/permissions/permissions.py:42 +msgid "Add comments to tasks" +msgstr "Dodaj komentarze do zadań" + +#: taiga/permissions/permissions.py:43 +msgid "Add issues" +msgstr "Dodaj zgłoszenia" + +#: taiga/permissions/permissions.py:44 +msgid "Add comments to issues" +msgstr "Dodaj komentarze do zgłoszeń" + +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +msgid "Add wiki page" +msgstr "Dodaj strony Wiki" + +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +msgid "Modify wiki page" +msgstr "Modyfikuj stronę Wiki" + +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +msgid "Add wiki link" +msgstr "Dodaj link do Wiki" + +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +msgid "Modify wiki link" +msgstr "Modyfikuj link do Wiki" + +#: taiga/permissions/permissions.py:55 +msgid "Add milestone" +msgstr "Dodaj kamień milowy" + +#: taiga/permissions/permissions.py:56 +msgid "Modify milestone" +msgstr "Modyfikuj Kamień milowy" + +#: taiga/permissions/permissions.py:57 +msgid "Delete milestone" +msgstr "Usuń kamień milowy" + +#: taiga/permissions/permissions.py:59 +msgid "View user story" +msgstr "Zobacz historyjkę użytkownika" + +#: taiga/permissions/permissions.py:60 +msgid "Add user story" +msgstr "Dodaj historyjkę użytkownika" + +#: taiga/permissions/permissions.py:61 +msgid "Modify user story" +msgstr "Modyfikuj historyjkę użytkownika" + +#: taiga/permissions/permissions.py:62 +msgid "Delete user story" +msgstr "Usuń historyjkę użytkownika" + +#: taiga/permissions/permissions.py:65 +msgid "Add task" +msgstr "Dodaj zadanie" + +#: taiga/permissions/permissions.py:66 +msgid "Modify task" +msgstr "Modyfikuj zadanie" + +#: taiga/permissions/permissions.py:67 +msgid "Delete task" +msgstr "Usuń zadanie" + +#: taiga/permissions/permissions.py:71 +msgid "Add issue" +msgstr "Dodaj zgłoszenie" + +#: taiga/permissions/permissions.py:72 +msgid "Modify issue" +msgstr "Modyfikuj zgłoszenie" + +#: taiga/permissions/permissions.py:73 +msgid "Delete issue" +msgstr "Usuń zgłoszenie" + +#: taiga/permissions/permissions.py:78 +msgid "Delete wiki page" +msgstr "Usuń stronę Wiki" + +#: taiga/permissions/permissions.py:83 +msgid "Delete wiki link" +msgstr "Usuń link Wiki" + +#: taiga/permissions/permissions.py:87 +msgid "Modify project" +msgstr "Modyfikuj projekt" + +#: taiga/permissions/permissions.py:88 +msgid "Add member" +msgstr "Dodaj członka zespołu" + +#: taiga/permissions/permissions.py:89 +msgid "Remove member" +msgstr "Usuń członka zespołu" + +#: taiga/permissions/permissions.py:90 +msgid "Delete project" +msgstr "Usuń projekt" + +#: taiga/permissions/permissions.py:91 +msgid "Admin project values" +msgstr "Administruj wartościami projektu" + +#: taiga/permissions/permissions.py:92 +msgid "Admin roles" +msgstr "Administruj rolami" + +#: taiga/projects/api.py:204 +msgid "Not valid template name" +msgstr "Nieprawidłowa nazwa szablonu" + +#: taiga/projects/api.py:207 +msgid "Not valid template description" +msgstr "Nieprawidłowy opis szablonu" + +#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +msgid "At least one of the user must be an active admin" +msgstr "Przynajmniej jeden użytkownik musi być aktywnym Administratorem" + +#: taiga/projects/api.py:505 +msgid "You don't have permisions to see that." +msgstr "Nie masz uprawnień by to zobaczyć." + +#: taiga/projects/attachments/api.py:47 +msgid "Non partial updates not supported" +msgstr "Aktualizacje częściowe nie są wspierane" + +#: taiga/projects/attachments/api.py:62 +msgid "Project ID not matches between object and project" +msgstr "ID nie pasuje pomiędzy obiektem a projektem" + +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 +#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/userstorage/models.py:25 +msgid "owner" +msgstr "właściciel" + +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 +#: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 +#: taiga/projects/models.py:338 taiga/projects/models.py:364 +#: taiga/projects/models.py:395 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 +#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +msgid "project" +msgstr "projekt" + +#: taiga/projects/attachments/models.py:56 +msgid "content type" +msgstr "typ zawartości" + +#: taiga/projects/attachments/models.py:58 +msgid "object id" +msgstr "id obiektu" + +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 +#: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 +#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 +#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +msgid "modified date" +msgstr "data modyfikacji" + +#: taiga/projects/attachments/models.py:69 +msgid "attached file" +msgstr "załączony plik" + +#: taiga/projects/attachments/models.py:72 +msgid "is deprecated" +msgstr "jest przestarzałe" + +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 +#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "opis" + +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 +#: taiga/projects/models.py:391 taiga/projects/models.py:418 +#: taiga/projects/models.py:453 taiga/projects/models.py:476 +#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +msgid "order" +msgstr "kolejność" + +#: taiga/projects/choices.py:21 +msgid "AppearIn" +msgstr "AppearIn" + +#: taiga/projects/choices.py:22 +msgid "Jitsi" +msgstr "Jitsi" + +#: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "Niestandardowy" + +#: taiga/projects/choices.py:24 +msgid "Talky" +msgstr "Talky" + +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "Tekst" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "Teks wielowierszowy" + +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 +#: taiga/projects/models.py:350 taiga/projects/models.py:389 +#: taiga/projects/models.py:414 taiga/projects/models.py:451 +#: taiga/projects/models.py:474 taiga/projects/models.py:497 +#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/users/models.py:183 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nazwa" + +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "typ" + +#: taiga/projects/custom_attributes/models.py:87 +msgid "values" +msgstr "wartości" + +#: taiga/projects/custom_attributes/models.py:97 +#: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 +msgid "user story" +msgstr "historyjka użytkownika" + +#: taiga/projects/custom_attributes/models.py:112 +msgid "task" +msgstr "zadanie" + +#: taiga/projects/custom_attributes/models.py:127 +msgid "issue" +msgstr "zgłoszenie" + +#: taiga/projects/custom_attributes/serializers.py:57 +msgid "Already exists one with the same name." +msgstr "Już istnieje jeden z taką nazwą." + +#: taiga/projects/history/api.py:70 +msgid "Comment already deleted" +msgstr "Komentarz został już usunięty" + +#: taiga/projects/history/api.py:89 +msgid "Comment not deleted" +msgstr "Komentarz nie został usunięty" + +#: taiga/projects/history/choices.py:27 +msgid "Change" +msgstr "Zmień" + +#: taiga/projects/history/choices.py:28 +msgid "Create" +msgstr "Utwórz" + +#: taiga/projects/history/choices.py:29 +msgid "Delete" +msgstr "Usuń" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:22 +#, python-format +msgid "%(role)s role points" +msgstr "%(role)s punkty roli" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:25 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:130 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:133 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:156 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:193 +msgid "from" +msgstr "od" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:31 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:141 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:144 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:162 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:179 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:199 +msgid "to" +msgstr "do" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:43 +msgid "Added new attachment" +msgstr "Dodano nowy załącznik" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:61 +msgid "Updated attachment" +msgstr "Zaktualizowany załącznik" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:67 +msgid "deprecated" +msgstr "przestarzałe" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:69 +msgid "not deprecated" +msgstr "nie przestarzałe" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:85 +msgid "Deleted attachment" +msgstr "Usuń załącznik" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:104 +msgid "added" +msgstr "dodane" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:109 +msgid "removed" +msgstr "usuniete" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 +#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +msgid "Unassigned" +msgstr "Nieprzypisane" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:211 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:86 +msgid "-deleted-" +msgstr "-usunięte-" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "to:" +msgstr "do:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "from:" +msgstr "od:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:26 +msgid "Added" +msgstr "Dodane" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:33 +msgid "Changed" +msgstr "Zmienione" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:40 +msgid "Deleted" +msgstr "Usunięte" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:54 +msgid "added:" +msgstr "dodane:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:57 +msgid "removed:" +msgstr "usunięte:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:62 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:79 +msgid "From:" +msgstr "Od:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:63 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:80 +msgid "To:" +msgstr "Do:" + +#: taiga/projects/history/templatetags/functions.py:26 +#: taiga/projects/wiki/models.py:32 +msgid "content" +msgstr "zawartość" + +#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/mixins/blocked.py:31 +msgid "blocked note" +msgstr "zaglokowana notatka" + +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "sprint" + +#: taiga/projects/issues/api.py:195 +msgid "You don't have permissions to set this sprint to this issue." +msgstr "Nie masz uprawnień do połączenia tego zgłoszenia ze sprintem." + +#: taiga/projects/issues/api.py:199 +msgid "You don't have permissions to set this status to this issue." +msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." + +#: taiga/projects/issues/api.py:203 +msgid "You don't have permissions to set this severity to this issue." +msgstr "Nie masz uprawnień do ustawienia rygoru dla tego zgłoszenia." + +#: taiga/projects/issues/api.py:207 +msgid "You don't have permissions to set this priority to this issue." +msgstr "Nie masz uprawnień do ustawienia priorytetu dla tego zgłoszenia." + +#: taiga/projects/issues/api.py:211 +msgid "You don't have permissions to set this type to this issue." +msgstr "Nie masz uprawnień do ustawienia typu dla tego zgłoszenia." + +#: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 +#: taiga/projects/userstories/models.py:57 +msgid "ref" +msgstr "ref" + +#: taiga/projects/issues/models.py:40 taiga/projects/tasks/models.py:39 +#: taiga/projects/userstories/models.py:67 +msgid "status" +msgstr "status" + +#: taiga/projects/issues/models.py:42 +msgid "severity" +msgstr "rygor" + +#: taiga/projects/issues/models.py:44 +msgid "priority" +msgstr "priorytet" + +#: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 +#: taiga/projects/userstories/models.py:60 +msgid "milestone" +msgstr "kamień milowy" + +#: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 +msgid "finished date" +msgstr "data zakończenia" + +#: taiga/projects/issues/models.py:60 taiga/projects/tasks/models.py:53 +#: taiga/projects/userstories/models.py:89 +msgid "subject" +msgstr "temat" + +#: taiga/projects/issues/models.py:64 taiga/projects/tasks/models.py:63 +#: taiga/projects/userstories/models.py:93 +msgid "assigned to" +msgstr "przypisane do" + +#: taiga/projects/issues/models.py:66 taiga/projects/tasks/models.py:67 +#: taiga/projects/userstories/models.py:103 +msgid "external reference" +msgstr "źródło zgłoszenia" + +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:416 +#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +msgid "slug" +msgstr "slug" + +#: taiga/projects/milestones/models.py:42 +msgid "estimated start date" +msgstr "szacowana data rozpoczecia" + +#: taiga/projects/milestones/models.py:43 +msgid "estimated finish date" +msgstr "szacowana data zakończenia" + +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 +#: taiga/projects/models.py:420 taiga/projects/models.py:503 +msgid "is closed" +msgstr "jest zamknięte" + +#: taiga/projects/milestones/models.py:52 +msgid "disponibility" +msgstr "dostępność" + +#: taiga/projects/milestones/models.py:75 +msgid "The estimated start must be previous to the estimated finish." +msgstr "Szacowana data rozpoczęcia musi być wcześniejsza niż data zakończenia." + +#: taiga/projects/milestones/validators.py:12 +msgid "There's no sprint with that id" +msgstr "Nie ma sprintu o takim ID" + +#: taiga/projects/mixins/blocked.py:29 +msgid "is blocked" +msgstr "jest zablokowane" + +#: taiga/projects/mixins/ordering.py:47 +#, python-brace-format +msgid "'{param}' parameter is mandatory" +msgstr "'{param}' parametr jest obowiązkowy" + +#: taiga/projects/mixins/ordering.py:51 +msgid "'project' parameter is mandatory" +msgstr "'project' parametr jest obowiązkowy" + +#: taiga/projects/models.py:59 +msgid "email" +msgstr "e-mail" + +#: taiga/projects/models.py:61 +msgid "create at" +msgstr "utwórz na" + +#: taiga/projects/models.py:63 taiga/users/models.py:128 +msgid "token" +msgstr "token" + +#: taiga/projects/models.py:69 +msgid "invitation extra text" +msgstr "dodatkowy tekst w zaproszeniu" + +#: taiga/projects/models.py:72 +msgid "user order" +msgstr "kolejność użytkowników" + +#: taiga/projects/models.py:78 +msgid "The user is already member of the project" +msgstr "Użytkownik już jest członkiem tego projektu" + +#: taiga/projects/models.py:93 +msgid "default points" +msgstr "domyślne punkty" + +#: taiga/projects/models.py:97 +msgid "default US status" +msgstr "domyślny status dla HU" + +#: taiga/projects/models.py:101 +msgid "default task status" +msgstr "domyślny status dla zadania" + +#: taiga/projects/models.py:104 +msgid "default priority" +msgstr "domyślny priorytet" + +#: taiga/projects/models.py:107 +msgid "default severity" +msgstr "domyślny rygor" + +#: taiga/projects/models.py:111 +msgid "default issue status" +msgstr "domyślny status dla zgłoszenia" + +#: taiga/projects/models.py:115 +msgid "default issue type" +msgstr "domyślny typ dla zgłoszenia" + +#: taiga/projects/models.py:136 +msgid "members" +msgstr "członkowie" + +#: taiga/projects/models.py:139 +msgid "total of milestones" +msgstr "wszystkich kamieni milowych" + +#: taiga/projects/models.py:140 +msgid "total story points" +msgstr "wszystkich punktów " + +#: taiga/projects/models.py:143 taiga/projects/models.py:570 +msgid "active backlog panel" +msgstr "aktywny panel backlog" + +#: taiga/projects/models.py:145 taiga/projects/models.py:572 +msgid "active kanban panel" +msgstr "aktywny panel Kanban" + +#: taiga/projects/models.py:147 taiga/projects/models.py:574 +msgid "active wiki panel" +msgstr "aktywny panel Wiki" + +#: taiga/projects/models.py:149 taiga/projects/models.py:576 +msgid "active issues panel" +msgstr "aktywny panel zgłoszeń " + +#: taiga/projects/models.py:152 taiga/projects/models.py:579 +msgid "videoconference system" +msgstr "system wideokonferencji" + +#: taiga/projects/models.py:154 taiga/projects/models.py:581 +msgid "videoconference extra data" +msgstr "dodatkowe dane dla wideokonferencji" + +#: taiga/projects/models.py:159 +msgid "creation template" +msgstr "szablon " + +#: taiga/projects/models.py:162 +msgid "anonymous permissions" +msgstr "uprawnienia anonimowych" + +#: taiga/projects/models.py:166 +msgid "user permissions" +msgstr "uprawnienia użytkownika" + +#: taiga/projects/models.py:169 +msgid "is private" +msgstr "jest prywatna" + +#: taiga/projects/models.py:180 +msgid "tags colors" +msgstr "kolory tagów" + +#: taiga/projects/models.py:339 +msgid "modules config" +msgstr "konfiguracja modułów" + +#: taiga/projects/models.py:358 +msgid "is archived" +msgstr "zarchiwizowane" + +#: taiga/projects/models.py:360 taiga/projects/models.py:422 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/users/models.py:113 +msgid "color" +msgstr "kolor" + +#: taiga/projects/models.py:362 +msgid "work in progress limit" +msgstr "limit postępu prac" + +#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +msgid "value" +msgstr "wartość" + +#: taiga/projects/models.py:567 +msgid "default owner's role" +msgstr "domyśla rola właściciela" + +#: taiga/projects/models.py:583 +msgid "default options" +msgstr "domyślne opcje" + +#: taiga/projects/models.py:584 +msgid "us statuses" +msgstr "statusy HU" + +#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/userstories/models.py:72 +msgid "points" +msgstr "pinkty" + +#: taiga/projects/models.py:586 +msgid "task statuses" +msgstr "statusy zadań" + +#: taiga/projects/models.py:587 +msgid "issue statuses" +msgstr "statusy zgłoszeń" + +#: taiga/projects/models.py:588 +msgid "issue types" +msgstr "typy zgłoszeń" + +#: taiga/projects/models.py:589 +msgid "priorities" +msgstr "priorytety" + +#: taiga/projects/models.py:590 +msgid "severities" +msgstr "rygory" + +#: taiga/projects/models.py:591 +msgid "roles" +msgstr "role" + +#: taiga/projects/notifications/choices.py:28 +msgid "Not watching" +msgstr "Nie obserwujesz" + +#: taiga/projects/notifications/choices.py:29 +msgid "Watching" +msgstr "Obserwujesz" + +#: taiga/projects/notifications/choices.py:30 +msgid "Ignoring" +msgstr "Ignorujesz" + +#: taiga/projects/notifications/mixins.py:87 +msgid "watchers" +msgstr "obserwatorzy" + +#: taiga/projects/notifications/models.py:59 +msgid "created date time" +msgstr "data utworzenia" + +#: taiga/projects/notifications/models.py:61 +msgid "updated date time" +msgstr "data aktualizacji" + +#: taiga/projects/notifications/models.py:63 +msgid "history entries" +msgstr "wpisy historii" + +#: taiga/projects/notifications/models.py:66 +msgid "notify users" +msgstr "powiadom użytkowników" + +#: taiga/projects/notifications/services.py:63 +#: taiga/projects/notifications/services.py:77 +msgid "Notify exists for specified user and project" +msgstr "Powiadomienie istnieje dla określonego użytkownika i projektu" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue updated

\n" +"

Hello %(user)s,
%(changer)s has updated an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +" " +msgstr "" +"\n" +"

Zgłoszenie zaktualizowane

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s zaktualizował zgłoszenie " +"w projekcie %(project)s

\n" +"

Zgłoszenie #%(ref)s %(subject)s

\n" +" Zobacz zgłoszenie\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Issue updated\n" +"Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"Zgłoszenie zaktualizowane\n" +"Witaj, użytkownik %(user)s, %(changer)s zaktualizował zgłoszenie w projekcie " +"%(project)s\n" +"Zobacz zgłoszenie #%(ref)s %(subject)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował zgłoszenie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New issue created

\n" +"

Hello %(user)s,
%(changer)s has created a new issue on " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Utworzono nowe zgłoszenie

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s utworzył nowe zgłoszenie " +"w projekcie %(project)s

\n" +"

Zgłoszenie #%(ref)s %(subject)s

\n" +" Zobacz zgłoszenie\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New issue created\n" +"Hello %(user)s, %(changer)s has created a new issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Utworzono nowe zgłoszenie\n" +"Witaj, użytkownik %(user)s, %(changer)s utworzył nowe zgłoszenie w projekcie " +"%(project)s\n" +"Zobacz zgłoszenie #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Utworzył zgłoszenie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zgłoszenie usunięte

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s usunął zgłoszenie w " +"projekcie %(project)s

\n" +"

Zgłoszenie #%(ref)s %(subject)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Issue deleted\n" +"Hello %(user)s, %(changer)s has deleted an issue on %(project)s\n" +"Issue #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Zgłoszenie usunięte\n" +"Witaj, użytkownik %(user)s, %(changer)s usunął zgłoszenie w projekcie " +"%(project)s\n" +"Zgłoszenie #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Usunął zgłoszenie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint updated

\n" +"

Hello %(user)s,
%(changer)s has updated an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See sprint\n" +" " +msgstr "" +"\n" +"

Sprint zaktualizowany

\n" +"

Witaj, uzytkownik %(user)s,
%(changer)s zaktualizował sprint w " +"projekcie %(project)s

\n" +"

Sprint %(name)s

\n" +" Zobacz sprint\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Sprint updated\n" +"Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +msgstr "" +"\n" +"Sprint zaktualizowany\n" +"Witaj, użytkownik %(user)s, %(changer)s zaktualizował sprint w projekcie " +"%(project)s\n" +"Zobacz sprint %(name)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował sprint\"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New sprint created

\n" +"

Hello %(user)s,
%(changer)s has created a new sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See " +"sprint\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Utworzono nowy sprint

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s utworzył nowy sprint w " +"projekcie %(project)s

\n" +"

Sprint %(name)s

\n" +" Zobacz sprint\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New sprint created\n" +"Hello %(user)s, %(changer)s has created a new sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Utworzono nowy sprint\n" +"Witaj, użytkownik %(user)s, %(changer)s utworzył nowy sprint w projekcie " +"%(project)s\n" +"Zobacz sprint %(name)s at %(url)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Utworzył sprint \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Sprint usunięty

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s usunął sprint w " +"projekcie %(project)s

\n" +"

Sprint %(name)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Sprint deleted\n" +"Hello %(user)s, %(changer)s has deleted an sprint on %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Sprint usunięty\n" +"Witaj, użytkownik %(user)s, %(changer)s usunął sprint w projekcie " +"%(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Skasował sprint \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task updated

\n" +"

Hello %(user)s,
%(changer)s has updated a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +" " +msgstr "" +"\n" +"

Zadanie zaktualizowane

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s zaktualizował zadanie w " +"projekcie %(project)s

\n" +"

Zadanie #%(ref)s %(subject)s

\n" +" Zobacz zadanie\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Task updated\n" +"Hello %(user)s, %(changer)s has updated a task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"Zadanie zaktualizowane\n" +"Witaj, użytkownik %(user)s, %(changer)s zaktualizował zadanie w projekcie " +"%(project)s\n" +"Zobacz zadanie #%(ref)s %(subject)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował zadanie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New task created

\n" +"

Hello %(user)s,
%(changer)s has created a new task on " +"%(project)s

\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Utworzono nowe zadanie

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s utworzył nowe zadanie w " +"projekcie %(project)s

\n" +"

Zadanie #%(ref)s %(subject)s

\n" +" Zobacz zadanie\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New task created\n" +"Hello %(user)s, %(changer)s has created a new task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Utworzono nowe zadanie\n" +"Witaj, użytkownik %(user)s, %(changer)s utworzył nowe zadanie w projekcie " +"%(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Utworzył zadanie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zadanie usunięte

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s usunął zadanie w " +"projekcie %(project)s

\n" +"

Zadanie #%(ref)s %(subject)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Task deleted\n" +"Hello %(user)s, %(changer)s has deleted a task on %(project)s\n" +"Task #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Zadanie usunięte\n" +"Witaj, użytkownik %(user)s, %(changer)s usunął zadanie w projekcie " +"%(project)s\n" +"Zadanie #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Usunął zadanie #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story updated

\n" +"

Hello %(user)s,
%(changer)s has updated a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +" " +msgstr "" +"\n" +"

Historyjka użytkownika zaktualizowana

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s zaktualizował historyjkę " +"użytkownika w projekcie %(project)s

\n" +"

Historyjka użytkownika #%(ref)s %(subject)s

\n" +" Zobacz historyjkę użytkownika\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"User story updated\n" +"Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"Histroyjka użytkownika zaktualizowana\n" +"Witaj, użytkownik %(user)s, %(changer)s zaktualizował historyjkę użytkownika " +"w projekcie%(project)s\n" +"Zobacz historyjkę użytkownika #%(ref)s %(subject)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował HU #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New user story created

\n" +"

Hello %(user)s,
%(changer)s has created a new user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Utworzono nową historyjkę użytkownika

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s utworzył nową historyjkę " +"użytkownika w projekcie %(project)s

\n" +"

Historyjka użytkownika #%(ref)s %(subject)s

\n" +" Zobacz historyjkę użytkownika\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New user story created\n" +"Hello %(user)s, %(changer)s has created a new user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Utworzono nową historyjkę użytkownika\n" +"Witaj, użytkownik %(user)s, %(changer)s utworzył nową historyjkę użytkownika " +"w projekcie %(project)s\n" +"Zobacz historyjkę użytkownika #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Utworzył HU #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Historyjka użytkownika usunięta

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s usunął historyjkę " +"użytkownika w projekcie %(project)s

\n" +"

Historyjka użytkonika #%(ref)s %(subject)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"User Story deleted\n" +"Hello %(user)s, %(changer)s has deleted a user story on %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Historyjka użytkownika usunięta\n" +"Witaj, użytkownik%(user)s, %(changer)s usunął historyjkę użytkownika w " +"projekcie %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Usunął HU #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki Page updated

\n" +"

Hello %(user)s,
%(changer)s has updated a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See Wiki Page\n" +" " +msgstr "" +"\n" +"

Strona Wiki zaktualizowana

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s zaktualizował stronę " +"Wiki w projekcie %(project)s

\n" +"

Strona Wiki %(page)s

\n" +" Zobacz stronę Wiki\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Wiki Page updated\n" +"\n" +"Hello %(user)s, %(changer)s has updated a wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +msgstr "" +"\n" +"Strona Wiki zaktualizowana\n" +"\n" +"Witaj, użytkownik %(user)s, %(changer)s zaktualizował stronę Wiki w " +"projekcie %(project)s\n" +"\n" +"Zobacz stronę Wiki %(page)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował stronę Wiki\"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New wiki page created

\n" +"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See " +"wiki page\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Utworzono nową stronę Wiki

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s utworzył nową stronę " +"Wiki w projekcie %(project)s

\n" +"

Strona Wiki %(page)s

\n" +" Zobacz stronę Wiki\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New wiki page created\n" +"\n" +"Hello %(user)s, %(changer)s has created a new wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Utworzono nową stronę Wiki\n" +"\n" +"Witaj, użytkownik %(user)s, %(changer)s utworzył nową stronę Wiki " +"%(project)s\n" +"\n" +"Zobacz stronę Wiki %(page)s at %(url)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Zaktualizował stronę Wiki \"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki page deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Strona Wiki usunięta

\n" +"

Witaj, użytkownik %(user)s,
%(changer)s usunął stronę Wiki w " +"projekcie %(project)s

\n" +"

Strona Wiki %(page)s

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Wiki page deleted\n" +"\n" +"Hello %(user)s, %(changer)s has deleted a wiki page on %(project)s\n" +"\n" +"Wiki page %(page)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Strona Wiki usunięta\n" +"\n" +"Witaj, użytkownik %(user)s, %(changer)s usunął stronę Wiki w projekcie " +"%(project)s\n" +"\n" +"Strona Wiki %(page)s\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Usunął stronę Wiki \"%(page)s\"\n" + +#: taiga/projects/notifications/validators.py:44 +msgid "Watchers contains invalid users" +msgstr "Obserwatorzy zawierają niepoprawnych użytkowników" + +#: taiga/projects/occ/mixins.py:35 +msgid "The version must be an integer" +msgstr "Wersja musi być integerem ;)" + +#: taiga/projects/occ/mixins.py:58 +msgid "The version parameter is not valid" +msgstr "Parametr wersji jest nieprawidłowy" + +#: taiga/projects/occ/mixins.py:74 +msgid "The version doesn't match with the current one" +msgstr "Podana wersja nie zgadza się z aktualną." + +#: taiga/projects/occ/mixins.py:93 +msgid "version" +msgstr "wersja" + +#: taiga/projects/permissions.py:39 +msgid "You can't leave the project if there are no more owners" +msgstr "Nie możesz opuścić projektu, jeśli jesteś jego jedynym właścicielem" + +#: taiga/projects/serializers.py:237 +msgid "Email address is already taken" +msgstr "Tena adres e-mail jest już w użyciu" + +#: taiga/projects/serializers.py:249 +msgid "Invalid role for the project" +msgstr "Nieprawidłowa rola w projekcie" + +#: taiga/projects/serializers.py:348 +msgid "Total milestones must be major or equal to zero" +msgstr "Łączna liczba kamieni milowych musi być większa lub równa zero" + +#: taiga/projects/serializers.py:405 +msgid "Default options" +msgstr "Domyślne opcje" + +#: taiga/projects/serializers.py:406 +msgid "User story's statuses" +msgstr "Statusy historyjek użytkownika" + +#: taiga/projects/serializers.py:407 +msgid "Points" +msgstr "Punkty" + +#: taiga/projects/serializers.py:408 +msgid "Task's statuses" +msgstr "Statusy zadań" + +#: taiga/projects/serializers.py:409 +msgid "Issue's statuses" +msgstr "Statusy zgłoszeń" + +#: taiga/projects/serializers.py:410 +msgid "Issue's types" +msgstr "Typu zgłoszeń" + +#: taiga/projects/serializers.py:411 +msgid "Priorities" +msgstr "Priorytety" + +#: taiga/projects/serializers.py:412 +msgid "Severities" +msgstr "Rygory" + +#: taiga/projects/serializers.py:413 +msgid "Roles" +msgstr "Role" + +#: taiga/projects/services/stats.py:72 +msgid "Future sprint" +msgstr "Przyszły sprint" + +#: taiga/projects/services/stats.py:89 +msgid "Project End" +msgstr "Zakończenie projektu" + +#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +msgid "You don't have permissions to set this sprint to this task." +msgstr "Nie masz uprawnień do ustawiania sprintu dla tego zadania." + +#: taiga/projects/tasks/api.py:93 +msgid "You don't have permissions to set this user story to this task." +msgstr "" +"Nie masz uprawnień do ustawiania historyjki użytkownika dla tego zadania" + +#: taiga/projects/tasks/api.py:96 +msgid "You don't have permissions to set this status to this task." +msgstr "Nie masz uprawnień do ustawiania statusu dla tego zadania" + +#: taiga/projects/tasks/models.py:56 +msgid "us order" +msgstr "kolejność HU" + +#: taiga/projects/tasks/models.py:58 +msgid "taskboard order" +msgstr "Kolejność tablicy zadań" + +#: taiga/projects/tasks/models.py:66 +msgid "is iocaine" +msgstr "Iokaina" + +#: taiga/projects/tasks/validators.py:12 +msgid "There's no task with that id" +msgstr "Nie ma zadania z takim ID" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:6 +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:4 +msgid "someone" +msgstr "ktoś" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:11 +#, python-format +msgid "" +"\n" +"

You have been invited to Taiga!

\n" +"

Hi! %(full_name)s has sent you an invitation to join project " +"%(project)s in Taiga.
Taiga is a Free, open Source Agile Project " +"Management Tool.

\n" +" " +msgstr "" +"\n" +"

Zostałeś zaproszony do Taiga

\n" +"

Cześć! użytkownik %(full_name)s wysłał Ci zaproszenie do projektu " +"%(project)s w narzędziu Taiga.

\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 +#, python-format +msgid "" +"\n" +"

And now a few words from the jolly good fellow or sistren
" +"who thought so kindly as to invite you

\n" +"

%(extra)s

\n" +" " +msgstr "" +"\n" +"

A poniżej kilka słów od kogoś kto był tak miły
i zechciał " +"zaprosić Cię do projektu :)

\n" +"

%(extra)s

\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation to Taiga" +msgstr "Zaakceptuj zaproszenie do Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation" +msgstr "Zaakceptuj zaproszenie" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:25 +msgid "The Taiga Team" +msgstr "Zespół Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:6 +#, python-format +msgid "" +"\n" +"You, or someone you know, has invited you to Taiga\n" +"\n" +"Hi! %(full_name)s has sent you an invitation to join a project called " +"%(project)s which is being managed on Taiga, a Free, open Source Agile " +"Project Management Tool.\n" +msgstr "" +"\n" +"Ty, lub ktoś kogo znasz wysłał zaproszenie do Taiga\n" +"\n" +"Cześć! Użytkownik %(full_name)s wysłał Ci zaproszenie do projektu " +"%(project)s utworzonego w narzędziu Taiga.\n" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 +#, python-format +msgid "" +"\n" +"And now a few words from the jolly good fellow or sistren who thought so " +"kindly as to invite you:\n" +"\n" +"%(extra)s\n" +" " +msgstr "" +"\n" +"A poniżej kilka słów od kogoś kto był tak miły
i zechciał zaprosić Cię " +"do projektu :)\n" +"\n" +"%(extra)s\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:18 +msgid "Accept your invitation to Taiga following this link:" +msgstr "Zaakceptuj zaproszenie do Taiga klikając w ten link: " + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:20 +msgid "" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/projects/templates/emails/membership_invitation-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Invitation to join to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] Zaproszenie do dołączenia, do projektu '%(project)s'\n" + +#: taiga/projects/templates/emails/membership_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

You have been added to a project

\n" +"

Hello %(full_name)s,
you have been added to the project " +"%(project)s

\n" +" Go to " +"project\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zostałeś dodany do projektu

\n" +"

Cześć %(full_name)s,
zostałeś dodany do projektu %(project)s\n" +" Idź do " +"projektu\n" +"

Zespół Taiga

\n" +" " + +#: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"You have been added to a project\n" +"Hello %(full_name)s,you have been added to the project %(project)s\n" +"\n" +"See project at %(url)s\n" +msgstr "" +"\n" +"Zostałeś dodany do projektu\n" +"Cześć %(full_name)s, zostałęś dodany do projektu %(project)s\n" +"\n" +"Zobacz projektu tutaj: %(url)s\n" + +#: taiga/projects/templates/emails/membership_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Added to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] Dodany do projektu '%(project)s'\n" + +#. Translators: Name of scrum project template. +#: taiga/projects/translations.py:28 +msgid "Scrum" +msgstr "Scrum" + +#. Translators: Description of scrum project template. +#: taiga/projects/translations.py:30 +msgid "" +"The agile product backlog in Scrum is a prioritized features list, " +"containing short descriptions of all functionality desired in the product. " +"When applying Scrum, it's not necessary to start a project with a lengthy, " +"upfront effort to document all requirements. The Scrum product backlog is " +"then allowed to grow and change as more is learned about the product and its " +"customers" +msgstr "" +"Scrum w oparciu o Taiga, które jest kompletnym narzędziem zawierającym " +"product backlog, opisy wszystkich funkcji i wskazówki to przyjemność. " +"Pracując w Scrumie nie musisz rozpoczynać projektu projektu od " +"dokumentowania wszystkiego a więc możesz zacząć pracować znacznie szybciej. " +"Product backlog jest na tyle elastyczny, że umożliwia szybki wzrost i oswaja " +"zmiany wraz z tym jak cały zespół poznaje specyfikę projektu i wymagania " +"klienta." + +#. Translators: Name of kanban project template. +#: taiga/projects/translations.py:33 +msgid "Kanban" +msgstr "Kanban" + +#. Translators: Description of kanban project template. +#: taiga/projects/translations.py:35 +msgid "" +"Kanban is a method for managing knowledge work with an emphasis on just-in-" +"time delivery while not overloading the team members. In this approach, the " +"process, from definition of a task to its delivery to the customer, is " +"displayed for participants to see and team members pull work from a queue." +msgstr "" +"Kanban jest metodą kierowania pracami, ze szczególnym naciskiem na dostawy " +"just-in-time, bez przeciążania członków zespołu. W tym podejściu, zadania są " +"wyświetlane dla klienta a członkowie zespołu wyciągają je z kolejki." + +#. Translators: User story point value (value = undefined) +#: taiga/projects/translations.py:43 +msgid "?" +msgstr "?" + +#. Translators: User story point value (value = 0) +#: taiga/projects/translations.py:45 +msgid "0" +msgstr "0" + +#. Translators: User story point value (value = 0.5) +#: taiga/projects/translations.py:47 +msgid "1/2" +msgstr "1/2" + +#. Translators: User story point value (value = 1) +#: taiga/projects/translations.py:49 +msgid "1" +msgstr "1" + +#. Translators: User story point value (value = 2) +#: taiga/projects/translations.py:51 +msgid "2" +msgstr "2" + +#. Translators: User story point value (value = 3) +#: taiga/projects/translations.py:53 +msgid "3" +msgstr "3" + +#. Translators: User story point value (value = 5) +#: taiga/projects/translations.py:55 +msgid "5" +msgstr "5" + +#. Translators: User story point value (value = 8) +#: taiga/projects/translations.py:57 +msgid "8" +msgstr "8" + +#. Translators: User story point value (value = 10) +#: taiga/projects/translations.py:59 +msgid "10" +msgstr "10" + +#. Translators: User story point value (value = 13) +#: taiga/projects/translations.py:61 +msgid "13" +msgstr "13" + +#. Translators: User story point value (value = 20) +#: taiga/projects/translations.py:63 +msgid "20" +msgstr "20" + +#. Translators: User story point value (value = 40) +#: taiga/projects/translations.py:65 +msgid "40" +msgstr "40" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:73 taiga/projects/translations.py:96 +#: taiga/projects/translations.py:112 +msgid "New" +msgstr "Nowe" + +#. Translators: User story status +#: taiga/projects/translations.py:76 +msgid "Ready" +msgstr "Gotowe" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:79 taiga/projects/translations.py:98 +#: taiga/projects/translations.py:114 +msgid "In progress" +msgstr "W toku" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:82 taiga/projects/translations.py:100 +#: taiga/projects/translations.py:116 +msgid "Ready for test" +msgstr "Gotowe do testów" + +#. Translators: User story status +#: taiga/projects/translations.py:85 +msgid "Done" +msgstr "Gotowe!" + +#. Translators: User story status +#: taiga/projects/translations.py:88 +msgid "Archived" +msgstr "Zarchiwizowane" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:102 taiga/projects/translations.py:118 +msgid "Closed" +msgstr "Zamknięte" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:104 taiga/projects/translations.py:120 +msgid "Needs Info" +msgstr "Potrzebne informacje" + +#. Translators: Issue status +#: taiga/projects/translations.py:122 +msgid "Postponed" +msgstr "Odroczone" + +#. Translators: Issue status +#: taiga/projects/translations.py:124 +msgid "Rejected" +msgstr "Odrzucone" + +#. Translators: Issue type +#: taiga/projects/translations.py:132 +msgid "Bug" +msgstr "Błąd" + +#. Translators: Issue type +#: taiga/projects/translations.py:134 +msgid "Question" +msgstr "Pytanie" + +#. Translators: Issue type +#: taiga/projects/translations.py:136 +msgid "Enhancement" +msgstr "Ulepszenie" + +#. Translators: Issue priority +#: taiga/projects/translations.py:144 +msgid "Low" +msgstr "Niski" + +#. Translators: Issue priority +#. Translators: Issue severity +#: taiga/projects/translations.py:146 taiga/projects/translations.py:159 +msgid "Normal" +msgstr "Normalny" + +#. Translators: Issue priority +#: taiga/projects/translations.py:148 +msgid "High" +msgstr "Wysoki" + +#. Translators: Issue severity +#: taiga/projects/translations.py:155 +msgid "Wishlist" +msgstr "Życzenie" + +#. Translators: Issue severity +#: taiga/projects/translations.py:157 +msgid "Minor" +msgstr "Pomniejsze" + +#. Translators: Issue severity +#: taiga/projects/translations.py:161 +msgid "Important" +msgstr "Istotne" + +#. Translators: Issue severity +#: taiga/projects/translations.py:163 +msgid "Critical" +msgstr "Krytyczne" + +#. Translators: User role +#: taiga/projects/translations.py:170 +msgid "UX" +msgstr "UX" + +#. Translators: User role +#: taiga/projects/translations.py:172 +msgid "Design" +msgstr "Design" + +#. Translators: User role +#: taiga/projects/translations.py:174 +msgid "Front" +msgstr "Front" + +#. Translators: User role +#: taiga/projects/translations.py:176 +msgid "Back" +msgstr "Back" + +#. Translators: User role +#: taiga/projects/translations.py:178 +msgid "Product Owner" +msgstr "Właściciel produktu" + +#. Translators: User role +#: taiga/projects/translations.py:180 +msgid "Stakeholder" +msgstr "Interesariusz" + +#: taiga/projects/userstories/api.py:134 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" +"Nie masz uprawnień do ustawiania sprintu dla tej historyjki użytkownika." + +#: taiga/projects/userstories/api.py:138 +msgid "You don't have permissions to set this status to this user story." +msgstr "" +"Nie masz uprawnień do ustawiania statusu do tej historyjki użytkownika." + +#: taiga/projects/userstories/api.py:212 +#, python-brace-format +msgid "" +"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " +"{subject}\")" +msgstr "" +"Generowanie historyjki użytkownika [HU #{ref} - {subject}](:us:{ref} \"HU " +"#{ref} - {subject}\")" + +#: taiga/projects/userstories/models.py:37 +msgid "role" +msgstr "rola" + +#: taiga/projects/userstories/models.py:75 +msgid "backlog order" +msgstr "Kolejność backlogu" + +#: taiga/projects/userstories/models.py:77 +#: taiga/projects/userstories/models.py:79 +msgid "sprint order" +msgstr "kolejność sprintu" + +#: taiga/projects/userstories/models.py:87 +msgid "finish date" +msgstr "data zakończenia" + +#: taiga/projects/userstories/models.py:95 +msgid "is client requirement" +msgstr "wymaganie klienta" + +#: taiga/projects/userstories/models.py:97 +msgid "is team requirement" +msgstr "wymaganie zespołu" + +#: taiga/projects/userstories/models.py:102 +msgid "generated from issue" +msgstr "wygenerowane ze zgłoszenia" + +#: taiga/projects/userstories/validators.py:28 +msgid "There's no user story with that id" +msgstr "Nie ma historyjki użytkownika z takim ID" + +#: taiga/projects/validators.py:28 +msgid "There's no project with that id" +msgstr "Nie ma projektu z takim ID" + +#: taiga/projects/validators.py:37 +msgid "There's no user story status with that id" +msgstr "Nie ma statusu historyjki użytkownika z takim ID" + +#: taiga/projects/validators.py:46 +msgid "There's no task status with that id" +msgstr "Nie ma statusu zadania z takim ID" + +#: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 +#: taiga/projects/votes/models.py:54 +msgid "Votes" +msgstr "Głosy" + +#: taiga/projects/votes/models.py:50 +msgid "votes" +msgstr "głosy" + +#: taiga/projects/votes/models.py:53 +msgid "Vote" +msgstr "Głos" + +#: taiga/projects/wiki/api.py:60 +msgid "'content' parameter is mandatory" +msgstr "Parametr 'zawartość' jest wymagany" + +#: taiga/projects/wiki/api.py:63 +msgid "'project_id' parameter is mandatory" +msgstr "Parametr 'id_projektu' jest wymagany" + +#: taiga/projects/wiki/models.py:36 +msgid "last modifier" +msgstr "ostatnio zmodyfikowane przez" + +#: taiga/projects/wiki/models.py:69 +msgid "href" +msgstr "href" + +#: taiga/users/admin.py:50 +msgid "Personal info" +msgstr "Informacje osobiste" + +#: taiga/users/admin.py:52 +msgid "Permissions" +msgstr "Uprawnienia" + +#: taiga/users/admin.py:53 +msgid "Important dates" +msgstr "Ważne daty" + +#: taiga/users/api.py:124 taiga/users/api.py:131 +msgid "Invalid username or email" +msgstr "Nieprawidłowa nazwa użytkownika lub adrs e-mail" + +#: taiga/users/api.py:140 +msgid "Mail sended successful!" +msgstr "E-mail wysłany poprawnie!" + +#: taiga/users/api.py:152 taiga/users/api.py:157 +msgid "Token is invalid" +msgstr "Nieprawidłowy token." + +#: taiga/users/api.py:178 +msgid "Current password parameter needed" +msgstr "Należy podać bieżące hasło" + +#: taiga/users/api.py:181 +msgid "New password parameter needed" +msgstr "Należy podać nowe hasło" + +#: taiga/users/api.py:184 +msgid "Invalid password length at least 6 charaters needed" +msgstr "" +"Nieprawidłowa długość hasła - wymagane jest co najmniej 6 znaków" + +#: taiga/users/api.py:187 +msgid "Invalid current password" +msgstr "Podałeś nieprawidłowe bieżące hasło" + +#: taiga/users/api.py:203 +msgid "Incomplete arguments" +msgstr "Pola niekompletne" + +#: taiga/users/api.py:208 +msgid "Invalid image format" +msgstr "Niepoprawny format obrazka" + +#: taiga/users/api.py:261 +msgid "Duplicated email" +msgstr "Zduplikowany adres e-mail" + +#: taiga/users/api.py:263 +msgid "Not valid email" +msgstr "Niepoprawny adres e-mail" + +#: taiga/users/api.py:283 taiga/users/api.py:289 +msgid "" +"Invalid, are you sure the token is correct and you didn't use it before?" +msgstr "" +"Niepoprawne, jesteś pewien, że token jest poprawny i nie używałeś go " +"wcześniej? " + +#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +msgid "Invalid, are you sure the token is correct?" +msgstr "Niepoprawne, jesteś pewien, że token jest poprawny?" + +#: taiga/users/models.py:69 +msgid "superuser status" +msgstr "status SUPERUSER" + +#: taiga/users/models.py:70 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" +"Oznacza, że ten użytkownik posiada wszystkie uprawnienia bez konieczności " +"ich przydzielania." + +#: taiga/users/models.py:100 +msgid "username" +msgstr "nazwa użytkownika" + +#: taiga/users/models.py:101 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" +msgstr "Wymagane. 30 znaków. Liter, cyfr i znaków /./-/_" + +#: taiga/users/models.py:104 +msgid "Enter a valid username." +msgstr "Wprowadź poprawną nazwę użytkownika" + +#: taiga/users/models.py:107 +msgid "active" +msgstr "aktywny" + +#: taiga/users/models.py:108 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "" +"Oznacza, że ten użytkownik ma być traktowany jako aktywny. Możesz to " +"odznaczyć zamiast usuwać konto." + +#: taiga/users/models.py:114 +msgid "biography" +msgstr "biografia" + +#: taiga/users/models.py:117 +msgid "photo" +msgstr "zdjęcie" + +#: taiga/users/models.py:118 +msgid "date joined" +msgstr "data dołączenia" + +#: taiga/users/models.py:120 +msgid "default language" +msgstr "domyślny język Taiga" + +#: taiga/users/models.py:122 +msgid "default theme" +msgstr "domyślny szablon Taiga" + +#: taiga/users/models.py:124 +msgid "default timezone" +msgstr "domyśla strefa czasowa" + +#: taiga/users/models.py:126 +msgid "colorize tags" +msgstr "kolory tagów" + +#: taiga/users/models.py:131 +msgid "email token" +msgstr "tokem e-mail" + +#: taiga/users/models.py:133 +msgid "new email address" +msgstr "nowy adres e-mail" + +#: taiga/users/models.py:188 +msgid "permissions" +msgstr "uprawnienia" + +#: taiga/users/serializers.py:59 +msgid "invalid" +msgstr "Niepoprawne" + +#: taiga/users/serializers.py:70 +msgid "Invalid username. Try with a different one." +msgstr "Niepoprawna nazwa użytkownika. Spróbuj podać inną." + +#: taiga/users/services.py:48 taiga/users/services.py:52 +msgid "Username or password does not matches user." +msgstr "Nazwa użytkownika lub hasło są nieprawidłowe" + +#: taiga/users/templates/emails/change_email-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Change your email

\n" +"

Hello %(full_name)s,
please confirm your email

\n" +" Confirm " +"email\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Zmiana adresu e-mail

\n" +"

Witaj %(full_name)s,
potwierdź swój adres e-mail

\n" +" Potwierdź e-mail\n" +"

You can ignore this message if you did not request.

\n" +"

Zespół Taiga

\n" +" " + +#: taiga/users/templates/emails/change_email-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, please confirm your email\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(full_name)s, potwierdź swój e-mail\n" +"\n" +"%(url)s\n" +"\n" +"Możesz zignorować tę wiadomość jeśli nie prosiłeś o jej wysłanie.\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/users/templates/emails/change_email-subject.jinja:1 +msgid "[Taiga] Change email" +msgstr "[Taiga] Zmienił e-mail" + +#: taiga/users/templates/emails/password_recovery-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Recover your password

\n" +"

Hello %(full_name)s,
you asked to recover your password

\n" +" Recover your password\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Odzyskiwanie hasła

\n" +"

Witaj %(full_name)s,
poprosiłeś o możliwość odzyskania hasła.\n" +" Odzyskaj " +"hasło\n" +"

Zignoruj tę wiadomość jeśli o nią nie prosiłeś.

\n" +"

Zespół taiga

\n" +" " + +#: taiga/users/templates/emails/password_recovery-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, you asked to recover your password\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Witaj %(full_name)s, poprosiłeś o możliwość odzyskania hasła.\n" +"\n" +"%(url)s\n" +"\n" +"Możesz zignorować tę wiadomość jeśli nie chcesz odzyskać hasła.\n" +"\n" +"---\n" +"Zespół Taiga\n" + +#: taiga/users/templates/emails/password_recovery-subject.jinja:1 +msgid "[Taiga] Password recovery" +msgstr "[Taiga] Odzyskał hasło" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:6 +msgid "" +"\n" +" \n" +"

Thank you for registering in Taiga

\n" +"

We hope you enjoy it

\n" +"

We built Taiga because we wanted the project management tool " +"that sits open on our computers all day long, to serve as a continued " +"reminder of why we love to collaborate, code and design.

\n" +"

We built it to be beautiful, elegant, simple to use and fun - " +"without forsaking flexibility and power.

\n" +" The taiga Team\n" +" \n" +" " +msgstr "" +"\n" +" \n" +"

Dziękujemy za rejestrację w Taiga

\n" +"

amy nadzieję, że Ci się spodoba

\n" +"

WZbudowaliśmy narzędzie Taiga z potrzeby posiadania " +"rozwiązania na którym, będziemy mogli pracować bez ograniczeń funkcjonalnych " +"i czerpiąc przyjemność z doświadczeń wizualnych. Taiga stale przypomina nam " +"dlaczego uwielbiamy współpracować, kodować i tworzyć fantastyczny design.\n" +"

Taiga stworzyliśmy tak, by było pięknie, elegancko, prosto w " +"użyciu i zabawnie - nie zaniedbując jednocześnie elastyczności i możliwości " +"funkcjonalnych.

\n" +" Zespół Taiga\n" +" \n" +" " + +#: taiga/users/templates/emails/registered_user-body-html.jinja:23 +#, python-format +msgid "" +"\n" +" You may remove your account from this service clicking " +"here\n" +" " +msgstr "" +"\n" +" Możesz usunąć swoje konto klikając tutaj\n" +" " + +#: taiga/users/templates/emails/registered_user-body-text.jinja:1 +msgid "" +"\n" +"Thank you for registering in Taiga\n" +"\n" +"We hope you enjoy it\n" +"\n" +"We built Taiga because we wanted the project management tool that sits open " +"on our computers all day long, to serve as a continued reminder of why we " +"love to collaborate, code and design.\n" +"\n" +"We built it to be beautiful, elegant, simple to use and fun - without " +"forsaking flexibility and power.\n" +"\n" +"--\n" +"The taiga Team\n" +msgstr "" +"\n" +"Dziękujemy za rejestrację w Taiga\n" +"\n" +"Mamy nadzieję, że Ci się spodoba\n" +"\n" +"Zbudowaliśmy narzędzie Taiga z potrzeby posiadania rozwiązania na którym, " +"będziemy mogli pracować bez ograniczeń funkcjonalnych i czerpiąc przyjemność " +"z doświadczeń wizualnych. Taiga stale przypomina nam dlaczego uwielbiamy " +"współpracować, kodować i tworzyć fantastyczny design.\n" +"\n" +"Taiga stworzyliśmy tak, by było pięknie, elegancko, prosto w użyciu i " +"zabawnie - nie zaniedbując jednocześnie elastyczności i możliwości " +"funkcjonalnych.\n" +"\n" +"--\n" +"Zespół Taiga\n" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:13 +#, python-format +msgid "" +"\n" +"You may remove your account from this service: %(url)s\n" +msgstr "" +"\n" +"Możesz usunąć swoje konto z tego serwisu: %(url)s\n" + +#: taiga/users/templates/emails/registered_user-subject.jinja:1 +msgid "You've been Taigatized!" +msgstr "Zostałeś zaTaigowany" + +#: taiga/users/validators.py:29 +msgid "There's no role with that id" +msgstr "Nie istnieje rola z takim ID" + +#: taiga/userstorage/api.py:50 +msgid "" +"Duplicate key value violates unique constraint. Key '{}' already exists." +msgstr "Duplikowanie wartości klucza. Klucz '{}' już istnieje." + +#: taiga/userstorage/models.py:30 +msgid "key" +msgstr "klucz" + +#: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 +msgid "URL" +msgstr "URL" + +#: taiga/webhooks/models.py:29 +msgid "secret key" +msgstr "sekretny klucz" + +#: taiga/webhooks/models.py:39 +msgid "status code" +msgstr "kod statusu" + +#: taiga/webhooks/models.py:40 +msgid "request data" +msgstr "data żądania" + +#: taiga/webhooks/models.py:41 +msgid "request headers" +msgstr "nagłówki żądań" + +#: taiga/webhooks/models.py:42 +msgid "response data" +msgstr "dane odpowiedzi" + +#: taiga/webhooks/models.py:43 +msgid "response headers" +msgstr "nagłówki odpowiedzi" + +#: taiga/webhooks/models.py:44 +msgid "duration" +msgstr "czas trwania" From c4bdbd9c8ecad3402a132460f34a353013fbe639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 27 Jul 2015 00:24:39 +0200 Subject: [PATCH 075/190] Improved system stats --- settings/common.py | 1 + settings/local.py.example | 1 + taiga/stats/api.py | 21 ++++++--- taiga/stats/services.py | 99 ++++++++++++++++++++++++++++++++------- 4 files changed, 98 insertions(+), 24 deletions(-) diff --git a/settings/common.py b/settings/common.py index 69048eba..1799c4b7 100644 --- a/settings/common.py +++ b/settings/common.py @@ -437,6 +437,7 @@ FEEDBACK_EMAIL = "support@taiga.io" # Stats module settings STATS_ENABLED = False +STATS_CACHE_TIMEOUT = 60*60 # In second # 0 notifications will work in a synchronous way # >0 an external process will check the pending notifications and will send them diff --git a/settings/local.py.example b/settings/local.py.example index 03fa20c6..95ce96fd 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -87,6 +87,7 @@ DATABASES = { # STATS MODULE #STATS_ENABLED = False +#FRONT_SITEMAP_CACHE_TIMEOUT = 60*60 # In second # SITEMAP # If is True /front/sitemap.xml show a valid sitemap of taiga-front client diff --git a/taiga/stats/api.py b/taiga/stats/api.py index dae5cfdd..43c96d5c 100644 --- a/taiga/stats/api.py +++ b/taiga/stats/api.py @@ -12,6 +12,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from collections import OrderedDict + +from django.conf import settings +from django.views.decorators.cache import cache_page from taiga.base.api import viewsets from taiga.base import response @@ -24,10 +28,15 @@ class SystemStatsViewSet(viewsets.ViewSet): permission_classes = (permissions.SystemStatsPermission,) def list(self, request, **kwargs): - stats = { - "total_users": services.get_total_users(), - "total_projects": services.get_total_projects(), - "total_userstories": services.grt_total_user_stories(), - "total_issues": services.get_total_issues(), - } + import ipdb; ipdb.set_trace() + stats = OrderedDict() + stats["users"] = services.get_users_stats() + stats["projects"] = services.get_projects_stats() + stats["userstories"] = services.get_user_stories_stats() return response.Ok(stats) + + def _get_cache_timeout(self): + return getattr(settings, "STATS_CACHE_TIMEOUT", 0) + + def dispatch(self, *args, **kwargs): + return cache_page(self._get_cache_timeout())(super().dispatch)(*args, **kwargs) diff --git a/taiga/stats/services.py b/taiga/stats/services.py index 11f41d37..37980d03 100644 --- a/taiga/stats/services.py +++ b/taiga/stats/services.py @@ -12,33 +12,96 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from contextlib import closing from django.apps import apps +from django.db import connection +from django.utils import timezone +from datetime import timedelta +from collections import OrderedDict -def get_total_projects(): +def get_users_stats(): + model = apps.get_model("users", "User") + queryset = model.objects.filter(is_active=True, is_system=False) + stats = OrderedDict() + + # Total + stats["total"] = queryset.count() + + # Average last 7 days + today = timezone.now() + seven_days_ago = today-timedelta(days=7) + stats["average_last_seven_days"] = (queryset.filter(date_joined__range=(seven_days_ago, today)) + .count()) / 7 + + # Graph: users last year + a_year_ago = timezone.now() - timedelta(days=365) + sql_query = """ + SELECT date_trunc('week', "filtered_users"."date_joined") AS "week", + count(*) + FROM (SELECT * + FROM "users_user" + WHERE "users_user"."is_active" = TRUE + AND "users_user"."is_system" = FALSE + AND "users_user"."date_joined" >= %s) AS "filtered_users" + GROUP BY "week" + ORDER BY "week"; + """ + with closing(connection.cursor()) as cursor: + cursor.execute(sql_query, [a_year_ago]) + rows = cursor.fetchall() + + counts_last_year_per_week = OrderedDict() + sumatory = queryset.filter(date_joined__lt=rows[0][0]).count() + for row in rows: + sumatory += row[1] + counts_last_year_per_week[str(row[0].date())] = sumatory + + stats["counts_last_year_per_week"] = counts_last_year_per_week + + return stats + + +def get_projects_stats(): model = apps.get_model("projects", "Project") queryset = model.objects.all() - return queryset.count() + stats = OrderedDict() + + stats["total"] = queryset.count() + + today = timezone.now() + seven_days_ago = today-timedelta(days=7) + stats["average_last_seven_days"] = (queryset.filter(created_date__range=(seven_days_ago, today)) + .count()) / 7 + + stats["total_with_backlog"] = (queryset.filter(is_backlog_activated=True, + is_kanban_activated=False) + .count()) + stats["percent_with_backlog"] = stats["total_with_backlog"] * 100 / stats["total"] + + stats["total_with_kanban"] = (queryset.filter(is_backlog_activated=False, + is_kanban_activated=True) + .count()) + stats["percent_with_kanban"] = stats["total_with_kanban"] * 100 / stats["total"] + + stats["total_with_backlog_and_kanban"] = (queryset.filter(is_backlog_activated=True, + is_kanban_activated=True) + .count()) + stats["percent_with_backlog_and_kanban"] = stats["total_with_backlog_and_kanban"] * 100 / stats["total"] + + return stats -def grt_total_user_stories(): +def get_user_stories_stats(): model = apps.get_model("userstories", "UserStory") queryset = model.objects.all() - return queryset.count() + stats = OrderedDict() + stats["total"] = queryset.count() -def get_total_issues(): - model = apps.get_model("issues", "Issue") - queryset = model.objects.all() - return queryset.count() - - -def get_total_users(only_active=True, no_system=True): - model = apps.get_model("users", "User") - queryset = model.objects.all() - if only_active: - queryset = queryset.filter(is_active=True) - if no_system: - queryset = queryset.filter(is_system=False) - return queryset.count() + today = timezone.now() + seven_days_ago = today-timedelta(days=7) + stats["average_last_seven_days"] = (queryset.filter(created_date__range=(seven_days_ago, today)) + .count()) / 7 + return stats From b75b88d750c3c21b6e347ed5b631619fcd2eaf14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 27 Jul 2015 17:39:53 +0200 Subject: [PATCH 076/190] Minor fix --- taiga/stats/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/taiga/stats/api.py b/taiga/stats/api.py index 43c96d5c..bb2948e4 100644 --- a/taiga/stats/api.py +++ b/taiga/stats/api.py @@ -28,7 +28,6 @@ class SystemStatsViewSet(viewsets.ViewSet): permission_classes = (permissions.SystemStatsPermission,) def list(self, request, **kwargs): - import ipdb; ipdb.set_trace() stats = OrderedDict() stats["users"] = services.get_users_stats() stats["projects"] = services.get_projects_stats() From 21153ea1aa186bb44e6c69ef110bf430962ecbff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 10 Jun 2015 12:21:54 +0200 Subject: [PATCH 077/190] Improve userstories/filters_data and issues/filter_data --- CHANGELOG.md | 3 +- taiga/base/api/generics.py | 7 +- taiga/base/filters.py | 113 ++++++-- taiga/projects/api.py | 6 - taiga/projects/issues/api.py | 112 ++++---- taiga/projects/issues/permissions.py | 1 + taiga/projects/issues/services.py | 261 ++++++++++++++++++ taiga/projects/permissions.py | 1 - taiga/projects/services/__init__.py | 1 - taiga/projects/services/filters.py | 127 --------- taiga/projects/userstories/api.py | 50 +++- taiga/projects/userstories/permissions.py | 1 + taiga/projects/userstories/services.py | 146 ++++++++++ .../test_projects_resource.py | 19 -- tests/integration/test_issues.py | 192 +++++++++++++ tests/integration/test_userstories.py | 132 ++++++++- 16 files changed, 919 insertions(+), 253 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6049676a..7ae3a7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,9 @@ - Add polish (pl) translation. ### Misc -- API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer +- API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) +- API: Improve and fix some errors in issues/filters_data and userstories/filters_data. - Lots of small and not so small bugfixes. diff --git a/taiga/base/api/generics.py b/taiga/base/api/generics.py index feeebf77..de834139 100644 --- a/taiga/base/api/generics.py +++ b/taiga/base/api/generics.py @@ -85,7 +85,7 @@ class GenericAPIView(pagination.PaginationMixin, many=many, partial=partial, context=context) - def filter_queryset(self, queryset): + def filter_queryset(self, queryset, filter_backends=None): """ Given a queryset, filter it with whichever filter backend is in use. @@ -94,7 +94,10 @@ class GenericAPIView(pagination.PaginationMixin, method if you want to apply the configured filtering backend to the default queryset. """ - for backend in self.get_filter_backends(): + #NOTE TAIGA: Added filter_backends to overwrite the default behavior. + + backends = filter_backends or self.get_filter_backends() + for backend in backends: queryset = backend().filter_queryset(self.request, queryset, self) return queryset diff --git a/taiga/base/filters.py b/taiga/base/filters.py index 208be999..cbfdbdef 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -29,6 +29,11 @@ logger = logging.getLogger(__name__) +##################################################################### +# Base and Mixins +##################################################################### + + class BaseFilterBackend(object): """ A base class from which all filter backend classes should inherit. @@ -95,6 +100,9 @@ class OrderByFilterMixin(QueryParamsFilterMixin): if field_name not in order_by_fields: return queryset + if raw_fieldname in ["owner", "-owner", "assigned_to", "-assigned_to"]: + raw_fieldname = "{}__full_name".format(raw_fieldname) + return super().filter_queryset(request, queryset.order_by(raw_fieldname), view) @@ -105,6 +113,10 @@ class FilterBackend(OrderByFilterMixin): pass +##################################################################### +# Permissions filters +##################################################################### + class PermissionBasedFilterBackend(FilterBackend): permission = None @@ -345,9 +357,84 @@ class IsProjectAdminFromWebhookLogFilterBackend(FilterBackend, BaseIsProjectAdmi return super().filter_queryset(request, queryset, view) +##################################################################### +# Generic Attributes filters +##################################################################### + +class BaseRelatedFieldsFilter(FilterBackend): + def __init__(self, filter_name=None): + if filter_name: + self.filter_name = filter_name + + def _prepare_filter_data(self, query_param_value): + def _transform_value(value): + try: + return int(value) + except: + if value in self._special_values_dict: + return self._special_values_dict[value] + raise exc.BadRequest() + + values = set([x.strip() for x in query_param_value.split(",")]) + values = map(_transform_value, values) + return list(values) + + def _get_queryparams(self, params): + raw_value = params.get(self.filter_name, None) + + if raw_value: + value = self._prepare_filter_data(raw_value) + + if None in value: + qs_in_kwargs = {"{}__in".format(self.filter_name): [v for v in value if v is not None]} + qs_isnull_kwargs = {"{}__isnull".format(self.filter_name): True} + return Q(**qs_in_kwargs) | Q(**qs_isnull_kwargs) + else: + return {"{}__in".format(self.filter_name): value} + + return None + + def filter_queryset(self, request, queryset, view): + query = self._get_queryparams(request.QUERY_PARAMS) + if query: + if isinstance(query, dict): + queryset = queryset.filter(**query) + else: + queryset = queryset.filter(query) + + return super().filter_queryset(request, queryset, view) + + +class OwnersFilter(BaseRelatedFieldsFilter): + filter_name = 'owner' + + +class AssignedToFilter(BaseRelatedFieldsFilter): + filter_name = 'assigned_to' + + +class StatusesFilter(BaseRelatedFieldsFilter): + filter_name = 'status' + + +class IssueTypesFilter(BaseRelatedFieldsFilter): + filter_name = 'type' + + +class PrioritiesFilter(BaseRelatedFieldsFilter): + filter_name = 'priority' + + +class SeveritiesFilter(BaseRelatedFieldsFilter): + filter_name = 'severity' + + class TagsFilter(FilterBackend): - def __init__(self, filter_name='tags'): - self.filter_name = filter_name + filter_name = 'tags' + + def __init__(self, filter_name=None): + if filter_name: + self.filter_name = filter_name def _get_tags_queryparams(self, params): tags = params.get(self.filter_name, None) @@ -364,25 +451,9 @@ class TagsFilter(FilterBackend): return super().filter_queryset(request, queryset, view) -class StatusFilter(FilterBackend): - def __init__(self, filter_name='status'): - self.filter_name = filter_name - - def _get_status_queryparams(self, params): - status = params.get(self.filter_name, None) - if status is not None: - status = set([x.strip() for x in status.split(",")]) - return list(status) - - return None - - def filter_queryset(self, request, queryset, view): - query_status = self._get_status_queryparams(request.QUERY_PARAMS) - if query_status: - queryset = queryset.filter(status__in=query_status) - - return super().filter_queryset(request, queryset, view) - +##################################################################### +# Text search filters +##################################################################### class QFilter(FilterBackend): def filter_queryset(self, request, queryset, view): diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 975dd4ea..94941cb8 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -160,12 +160,6 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): self.check_permissions(request, "issues_stats", project) return response.Ok(services.get_stats_for_project_issues(project)) - @detail_route(methods=["GET"]) - def issue_filters_data(self, request, pk=None): - project = self.get_object() - self.check_permissions(request, "issues_filters_data", project) - return response.Ok(services.get_issues_filters_data(project)) - @detail_route(methods=["GET"]) def tags_colors(self, request, pk=None): project = self.get_object() diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index 6f59a93a..bb377e8d 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -42,79 +42,35 @@ from . import permissions from . import serializers -class IssuesFilter(filters.FilterBackend): - filter_fields = ("status", "severity", "priority", "owner", "assigned_to", "tags", "type") - _special_values_dict = { - 'true': True, - 'false': False, - 'null': None, - } - - def _prepare_filters_data(self, request): - def _transform_value(value): - try: - return int(value) - except: - if value in self._special_values_dict.keys(): - return self._special_values_dict[value] - raise exc.BadRequest() - - data = {} - for filtername in self.filter_fields: - if filtername not in request.QUERY_PARAMS: - continue - - raw_value = request.QUERY_PARAMS[filtername] - values = set([x.strip() for x in raw_value.split(",")]) - - if filtername != "tags": - values = map(_transform_value, values) - - data[filtername] = list(values) - return data - - def filter_queryset(self, request, queryset, view): - filterdata = self._prepare_filters_data(request) - - if "tags" in filterdata: - queryset = queryset.filter(tags__contains=filterdata["tags"]) - - for name, value in filter(lambda x: x[0] != "tags", filterdata.items()): - if None in value: - qs_in_kwargs = {"{0}__in".format(name): [v for v in value if v is not None]} - qs_isnull_kwargs = {"{0}__isnull".format(name): True} - queryset = queryset.filter(Q(**qs_in_kwargs) | Q(**qs_isnull_kwargs)) - else: - qs_kwargs = {"{0}__in".format(name): value} - queryset = queryset.filter(**qs_kwargs) - - return queryset - - -class IssuesOrdering(filters.FilterBackend): - def filter_queryset(self, request, queryset, view): - order_by = request.QUERY_PARAMS.get('order_by', None) - - if order_by in ['owner', '-owner', 'assigned_to', '-assigned_to']: - return queryset.order_by( - '{}__full_name'.format(order_by) - ) - return queryset - - class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): serializer_class = serializers.IssueNeighborsSerializer list_serializer_class = serializers.IssueSerializer permission_classes = (permissions.IssuePermission, ) - filter_backends = (filters.CanViewIssuesFilterBackend, filters.QFilter, - IssuesFilter, IssuesOrdering,) - retrieve_exclude_filters = (IssuesFilter,) + filter_backends = (filters.CanViewIssuesFilterBackend, + filters.OwnersFilter, + filters.AssignedToFilter, + filters.StatusesFilter, + filters.IssueTypesFilter, + filters.SeveritiesFilter, + filters.PrioritiesFilter, + filters.TagsFilter, + filters.QFilter, + filters.OrderByFilterMixin) + retrieve_exclude_filters = (filters.OwnersFilter, + filters.AssignedToFilter, + filters.StatusesFilter, + filters.IssueTypesFilter, + filters.SeveritiesFilter, + filters.PrioritiesFilter, + filters.TagsFilter,) - filter_fields = ("project", "status__is_closed", "watchers") + filter_fields = ("project", + "status__is_closed", + "watchers") order_by_fields = ("type", - "severity", "status", + "severity", "priority", "created_date", "modified_date", @@ -218,6 +174,32 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, issue = get_object_or_404(models.Issue, ref=ref, project_id=project_id) return self.retrieve(request, pk=issue.pk) + @list_route(methods=["GET"]) + def filters_data(self, request, *args, **kwargs): + project_id = request.QUERY_PARAMS.get("project", None) + project = get_object_or_404(Project, id=project_id) + + filter_backends = self.get_filter_backends() + types_filter_backends = (f for f in filter_backends if f != filters.IssueTypesFilter) + statuses_filter_backends = (f for f in filter_backends if f != filters.StatusesFilter) + assigned_to_filter_backends = (f for f in filter_backends if f != filters.AssignedToFilter) + owners_filter_backends = (f for f in filter_backends if f != filters.OwnersFilter) + priorities_filter_backends = (f for f in filter_backends if f != filters.PrioritiesFilter) + severities_filter_backends = (f for f in filter_backends if f != filters.SeveritiesFilter) + tags_filter_backends = (f for f in filter_backends if f != filters.TagsFilter) + + queryset = self.get_queryset() + querysets = { + "types": self.filter_queryset(queryset, filter_backends=types_filter_backends), + "statuses": self.filter_queryset(queryset, filter_backends=statuses_filter_backends), + "assigned_to": self.filter_queryset(queryset, filter_backends=assigned_to_filter_backends), + "owners": self.filter_queryset(queryset, filter_backends=owners_filter_backends), + "priorities": self.filter_queryset(queryset, filter_backends=priorities_filter_backends), + "severities": self.filter_queryset(queryset, filter_backends=severities_filter_backends), + "tags": self.filter_queryset(queryset) + } + return response.Ok(services.get_issues_filters_data(project, querysets)) + @list_route(methods=["GET"]) def csv(self, request): uuid = request.QUERY_PARAMS.get("uuid", None) diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 0a106092..0eecf121 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -28,6 +28,7 @@ class IssuePermission(TaigaResourcePermission): update_perms = HasProjectPerm('modify_issue') destroy_perms = HasProjectPerm('delete_issue') list_perms = AllowAny() + filters_data_perms = AllowAny() csv_perms = AllowAny() upvote_perms = IsAuthenticated() & HasProjectPerm('vote_issues') downvote_perms = IsAuthenticated() & HasProjectPerm('vote_issues') diff --git a/taiga/projects/issues/services.py b/taiga/projects/issues/services.py index d659b527..7d6f3061 100644 --- a/taiga/projects/issues/services.py +++ b/taiga/projects/issues/services.py @@ -16,6 +16,12 @@ import io import csv +from collections import OrderedDict +from operator import itemgetter +from contextlib import closing + +from django.db import connection +from django.utils.translation import ugettext as _ from taiga.base.utils import db, text @@ -101,3 +107,258 @@ def issues_to_csv(project, queryset): writer.writerow(issue_data) return csv_data + + +def _get_issues_statuses(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "projects_issuestatus"."id", + "projects_issuestatus"."name", + "projects_issuestatus"."color", + "projects_issuestatus"."order", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id") + WHERE {where} AND "issues_issue"."status_id" = "projects_issuestatus"."id") + FROM "projects_issuestatus" + WHERE "projects_issuestatus"."project_id" = %s + ORDER BY "projects_issuestatus"."order"; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, name, color, order, count in rows: + result.append({ + "id": id, + "name": _(name), + "color": color, + "order": order, + "count": count, + }) + return sorted(result, key=itemgetter("order")) + + +def _get_issues_types(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "projects_issuetype"."id", + "projects_issuetype"."name", + "projects_issuetype"."color", + "projects_issuetype"."order", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id") + WHERE {where} AND "issues_issue"."type_id" = "projects_issuetype"."id") + FROM "projects_issuetype" + WHERE "projects_issuetype"."project_id" = %s + ORDER BY "projects_issuetype"."order"; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, name, color, order, count in rows: + result.append({ + "id": id, + "name": _(name), + "color": color, + "order": order, + "count": count, + }) + return sorted(result, key=itemgetter("order")) + + +def _get_issues_priorities(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "projects_priority"."id", + "projects_priority"."name", + "projects_priority"."color", + "projects_priority"."order", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id") + WHERE {where} AND "issues_issue"."priority_id" = "projects_priority"."id") + FROM "projects_priority" + WHERE "projects_priority"."project_id" = %s + ORDER BY "projects_priority"."order"; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, name, color, order, count in rows: + result.append({ + "id": id, + "name": _(name), + "color": color, + "order": order, + "count": count, + }) + return sorted(result, key=itemgetter("order")) + + +def _get_issues_severities(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "projects_severity"."id", + "projects_severity"."name", + "projects_severity"."color", + "projects_severity"."order", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id") + WHERE {where} AND "issues_issue"."severity_id" = "projects_severity"."id") + FROM "projects_severity" + WHERE "projects_severity"."project_id" = %s + ORDER BY "projects_severity"."order"; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, name, color, order, count in rows: + result.append({ + "id": id, + "name": _(name), + "color": color, + "order": order, + "count": count, + }) + return sorted(result, key=itemgetter("order")) + + +def _get_issues_assigned_to(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT NULL, + NULL, + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id" ) + WHERE {where} AND "issues_issue"."assigned_to_id" IS NULL) + UNION SELECT "users_user"."id", + "users_user"."full_name", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id" ) + WHERE {where} AND "issues_issue"."assigned_to_id" = "projects_membership"."user_id") + FROM "projects_membership" + INNER JOIN "users_user" ON + ("projects_membership"."user_id" = "users_user"."id") + WHERE "projects_membership"."project_id" = %s AND "projects_membership"."user_id" IS NOT NULL; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, full_name, count in rows: + result.append({ + "id": id, + "full_name": full_name or "", + "count": count, + }) + return sorted(result, key=itemgetter("full_name")) + + +def _get_issues_owners(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "users_user"."id", + "users_user"."full_name", + (SELECT count(*) + FROM "issues_issue" + INNER JOIN "projects_project" ON + ("issues_issue"."project_id" = "projects_project"."id") + WHERE {where} and "issues_issue"."owner_id" = "projects_membership"."user_id") + FROM "projects_membership" + RIGHT OUTER JOIN "users_user" ON + ("projects_membership"."user_id" = "users_user"."id") + WHERE ("projects_membership"."project_id" = %s AND "projects_membership"."user_id" IS NOT NULL) + OR ("users_user"."is_system" IS TRUE); + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, full_name, count in rows: + if count > 0: + result.append({ + "id": id, + "full_name": full_name, + "count": count, + }) + return sorted(result, key=itemgetter("full_name")) + + +def _get_issues_tags(queryset): + tags = [] + for t_list in queryset.values_list("tags", flat=True): + if t_list is None: + continue + tags += list(t_list) + + tags = [{"name":e, "count":tags.count(e)} for e in set(tags)] + + return sorted(tags, key=itemgetter("name")) + + +def get_issues_filters_data(project, querysets): + """ + Given a project and an issues queryset, return a simple data structure + of all possible filters for the issues in the queryset. + """ + data = OrderedDict([ + ("types", _get_issues_types(project, querysets["types"])), + ("statuses", _get_issues_statuses(project, querysets["statuses"])), + ("priorities", _get_issues_priorities(project, querysets["priorities"])), + ("severities", _get_issues_severities(project, querysets["severities"])), + ("assigned_to", _get_issues_assigned_to(project, querysets["assigned_to"])), + ("owners", _get_issues_owners(project, querysets["owners"])), + ("tags", _get_issues_tags(querysets["tags"])), + ]) + + return data diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 5483e3d6..943ef379 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -60,7 +60,6 @@ class ProjectPermission(TaigaResourcePermission): star_perms = IsAuthenticated() unstar_perms = IsAuthenticated() issues_stats_perms = HasProjectPerm('view_project') - issues_filters_data_perms = HasProjectPerm('view_project') tags_perms = HasProjectPerm('view_project') tags_colors_perms = HasProjectPerm('view_project') fans_perms = HasProjectPerm('view_project') diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index 12b4276c..8a7284d9 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -27,7 +27,6 @@ from .bulk_update_order import bulk_update_points_order from .bulk_update_order import bulk_update_userstory_status_order from .filters import get_all_tags -from .filters import get_issues_filters_data from .stats import get_stats_for_project_issues from .stats import get_stats_for_project diff --git a/taiga/projects/services/filters.py b/taiga/projects/services/filters.py index e8c2786c..5a2d1ff3 100644 --- a/taiga/projects/services/filters.py +++ b/taiga/projects/services/filters.py @@ -50,113 +50,6 @@ def _get_issues_tags(project): return result -def _get_issues_tags_with_count(project): - extra_sql = ("select unnest(tags) as tagname, count(unnest(tags)) " - "from issues_issue where project_id = %s " - "group by unnest(tags) " - "order by tagname asc") - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - - -def _get_issues_statuses(project): - extra_sql = ("select status_id, count(status_id) from issues_issue " - "where project_id = %s group by status_id;") - - extra_sql = """ - select id, (select count(*) from issues_issue - where project_id = m.project_id and status_id = m.id) - from projects_issuestatus as m - where project_id = %s order by m.order; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - - -def _get_issues_priorities(project): - extra_sql = """ - select id, (select count(*) from issues_issue - where project_id = m.project_id and priority_id = m.id) - from projects_priority as m - where project_id = %s order by m.order; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - -def _get_issues_types(project): - extra_sql = """ - select id, (select count(*) from issues_issue - where project_id = m.project_id and type_id = m.id) - from projects_issuetype as m - where project_id = %s order by m.order; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - - -def _get_issues_severities(project): - extra_sql = """ - select id, (select count(*) from issues_issue - where project_id = m.project_id and severity_id = m.id) - from projects_severity as m - where project_id = %s order by m.order; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - - -def _get_issues_assigned_to(project): - extra_sql = """ - select null, (select count(*) from issues_issue - where project_id = %s and assigned_to_id is null) - UNION select user_id, (select count(*) from issues_issue - where project_id = pm.project_id and assigned_to_id = pm.user_id) - from projects_membership as pm - where project_id = %s and pm.user_id is not null; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id, project.id]) - rows = cursor.fetchall() - - return rows - - -def _get_issues_owners(project): - extra_sql = """ - select user_id, (select count(*) from issues_issue - where project_id = pm.project_id and owner_id = pm.user_id) - from projects_membership as pm - where project_id = %s and pm.user_id is not null; - """ - - with closing(connection.cursor()) as cursor: - cursor.execute(extra_sql, [project.id]) - rows = cursor.fetchall() - - return rows - - # Public api def get_all_tags(project): @@ -170,23 +63,3 @@ def get_all_tags(project): result.update(_get_stories_tags(project)) result.update(_get_tasks_tags(project)) return sorted(result) - - -def get_issues_filters_data(project): - """ - Given a project, return a simple data structure - of all possible filters for issues. - """ - - data = { - "types": _get_issues_types(project), - "statuses": _get_issues_statuses(project), - "priorities": _get_issues_priorities(project), - "severities": _get_issues_severities(project), - "assigned_to": _get_issues_assigned_to(project), - "created_by": _get_issues_owners(project), - "owners": _get_issues_owners(project), - "tags": _get_issues_tags_with_count(project), - } - - return data diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 9820f589..0467ef16 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -49,15 +49,27 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi serializer_class = serializers.UserStoryNeighborsSerializer list_serializer_class = serializers.UserStorySerializer permission_classes = (permissions.UserStoryPermission,) - - filter_backends = (filters.StatusFilter, filters.CanViewUsFilterBackend, filters.TagsFilter, - filters.QFilter, filters.OrderByFilterMixin) - - retrieve_exclude_filters = (filters.StatusFilter, filters.TagsFilter,) - filter_fields = ["project", "milestone", "milestone__isnull", - "is_archived", "status__is_archived", "assigned_to", - "status__is_closed", "watchers", "is_closed"] - order_by_fields = ["backlog_order", "sprint_order", "kanban_order"] + filter_backends = (filters.CanViewUsFilterBackend, + filters.OwnersFilter, + filters.AssignedToFilter, + filters.StatusesFilter, + filters.TagsFilter, + filters.QFilter, + filters.OrderByFilterMixin) + retrieve_exclude_filters = (filters.OwnersFilter, + filters.AssignedToFilter, + filters.StatusesFilter, + filters.TagsFilter) + filter_fields = ["project", + "milestone", + "milestone__isnull", + "is_closed", + "status__is_archived", + "status__is_closed", + "watchers"] + order_by_fields = ["backlog_order", + "sprint_order", + "kanban_order"] # Specific filter used for filtering neighbor user stories _neighbor_tags_filter = filters.TagsFilter('neighbor_tags') @@ -138,6 +150,26 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi raise exc.PermissionDenied(_("You don't have permissions to set this status " "to this user story.")) + @list_route(methods=["GET"]) + def filters_data(self, request, *args, **kwargs): + project_id = request.QUERY_PARAMS.get("project", None) + project = get_object_or_404(Project, id=project_id) + + filter_backends = self.get_filter_backends() + statuses_filter_backends = (f for f in filter_backends if f != filters.StatusesFilter) + assigned_to_filter_backends = (f for f in filter_backends if f != filters.AssignedToFilter) + owners_filter_backends = (f for f in filter_backends if f != filters.OwnersFilter) + tags_filter_backends = (f for f in filter_backends if f != filters.TagsFilter) + + queryset = self.get_queryset() + querysets = { + "statuses": self.filter_queryset(queryset, filter_backends=statuses_filter_backends), + "assigned_to": self.filter_queryset(queryset, filter_backends=assigned_to_filter_backends), + "owners": self.filter_queryset(queryset, filter_backends=owners_filter_backends), + "tags": self.filter_queryset(queryset) + } + return response.Ok(services.get_userstories_filters_data(project, querysets)) + @list_route(methods=["GET"]) def by_ref(self, request): ref = request.QUERY_PARAMS.get("ref", None) diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 3b836cb6..32c14e01 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -25,6 +25,7 @@ class UserStoryPermission(TaigaResourcePermission): update_perms = HasProjectPerm('modify_us') destroy_perms = HasProjectPerm('delete_us') list_perms = AllowAny() + filters_data_perms = AllowAny() csv_perms = AllowAny() bulk_create_perms = IsAuthenticated() & (HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us')) bulk_update_order_perms = HasProjectPerm('modify_us') diff --git a/taiga/projects/userstories/services.py b/taiga/projects/userstories/services.py index 13624162..9de3bf53 100644 --- a/taiga/projects/userstories/services.py +++ b/taiga/projects/userstories/services.py @@ -16,8 +16,13 @@ import csv import io +from collections import OrderedDict +from operator import itemgetter +from contextlib import closing +from django.db import connection from django.utils import timezone +from django.utils.translation import ugettext as _ from taiga.base.utils import db, text from taiga.projects.history.services import take_snapshot @@ -170,3 +175,144 @@ def userstories_to_csv(project,queryset): writer.writerow(row) return csv_data + + +def _get_userstories_statuses(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "projects_userstorystatus"."id", + "projects_userstorystatus"."name", + "projects_userstorystatus"."color", + "projects_userstorystatus"."order", + (SELECT count(*) + FROM "userstories_userstory" + INNER JOIN "projects_project" ON + ("userstories_userstory"."project_id" = "projects_project"."id") + WHERE {where} AND "userstories_userstory"."status_id" = "projects_userstorystatus"."id") + FROM "projects_userstorystatus" + WHERE "projects_userstorystatus"."project_id" = %s + ORDER BY "projects_userstorystatus"."order"; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, name, color, order, count in rows: + result.append({ + "id": id, + "name": _(name), + "color": color, + "order": order, + "count": count, + }) + return sorted(result, key=itemgetter("order")) + + +def _get_userstories_assigned_to(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT NULL, + NULL, + (SELECT count(*) + FROM "userstories_userstory" + INNER JOIN "projects_project" ON + ("userstories_userstory"."project_id" = "projects_project"."id" ) + WHERE {where} AND "userstories_userstory"."assigned_to_id" IS NULL) + UNION SELECT "users_user"."id", + "users_user"."full_name", + (SELECT count(*) + FROM "userstories_userstory" + INNER JOIN "projects_project" ON + ("userstories_userstory"."project_id" = "projects_project"."id" ) + WHERE {where} AND "userstories_userstory"."assigned_to_id" = "projects_membership"."user_id") + FROM "projects_membership" + INNER JOIN "users_user" ON + ("projects_membership"."user_id" = "users_user"."id") + WHERE "projects_membership"."project_id" = %s AND "projects_membership"."user_id" IS NOT NULL; + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, full_name, count in rows: + result.append({ + "id": id, + "full_name": full_name or "", + "count": count, + }) + return sorted(result, key=itemgetter("full_name")) + + +def _get_userstories_owners(project, queryset): + compiler = connection.ops.compiler(queryset.query.compiler)(queryset.query, connection, None) + queryset_where_tuple = queryset.query.where.as_sql(compiler, connection) + where = queryset_where_tuple[0] + where_params = queryset_where_tuple[1] + + extra_sql = """ + SELECT "users_user"."id", + "users_user"."full_name", + (SELECT count(*) + FROM "userstories_userstory" + INNER JOIN "projects_project" ON + ("userstories_userstory"."project_id" = "projects_project"."id") + WHERE {where} AND "userstories_userstory"."owner_id" = "projects_membership"."user_id") + FROM "projects_membership" + RIGHT OUTER JOIN "users_user" ON + ("projects_membership"."user_id" = "users_user"."id") + WHERE (("projects_membership"."project_id" = %s AND "projects_membership"."user_id" IS NOT NULL) + OR ("users_user"."is_system" IS TRUE)); + """.format(where=where) + + with closing(connection.cursor()) as cursor: + cursor.execute(extra_sql, where_params + [project.id]) + rows = cursor.fetchall() + + result = [] + for id, full_name, count in rows: + if count > 0: + result.append({ + "id": id, + "full_name": full_name, + "count": count, + }) + return sorted(result, key=itemgetter("full_name")) + + +def _get_userstories_tags(queryset): + tags = [] + for t_list in queryset.values_list("tags", flat=True): + if t_list is None: + continue + tags += list(t_list) + + tags = [{"name":e, "count":tags.count(e)} for e in set(tags)] + + return sorted(tags, key=itemgetter("name")) + + +def get_userstories_filters_data(project, querysets): + """ + Given a project and an userstories queryset, return a simple data structure + of all possible filters for the userstories in the queryset. + """ + data = OrderedDict([ + ("statuses", _get_userstories_statuses(project, querysets["statuses"])), + ("assigned_to", _get_userstories_assigned_to(project, querysets["assigned_to"])), + ("owners", _get_userstories_owners(project, querysets["owners"])), + ("tags", _get_userstories_tags(querysets["tags"])), + ]) + + return data diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 0c97c952..575e0d62 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -255,25 +255,6 @@ def test_project_action_issues_stats(client, data): assert results == [404, 404, 200, 200] -def test_project_action_issues_filters_data(client, data): - public_url = reverse('projects-issue-filters-data', kwargs={"pk": data.public_project.pk}) - private1_url = reverse('projects-issue-filters-data', kwargs={"pk": data.private_project1.pk}) - private2_url = reverse('projects-issue-filters-data', kwargs={"pk": data.private_project2.pk}) - - users = [ - None, - data.registered_user, - data.project_member_with_perms, - data.project_owner - ] - results = helper_test_http_method(client, 'get', public_url, None, users) - assert results == [200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private1_url, None, users) - assert results == [200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private2_url, None, users) - assert results == [404, 404, 200, 200] - - def test_project_action_fans(client, data): public_url = reverse('projects-fans', kwargs={"pk": data.public_project.pk}) private1_url = reverse('projects-fans', kwargs={"pk": data.private_project1.pk}) diff --git a/tests/integration/test_issues.py b/tests/integration/test_issues.py index a3aa9db0..84727427 100644 --- a/tests/integration/test_issues.py +++ b/tests/integration/test_issues.py @@ -189,6 +189,198 @@ def test_api_filter_by_text_6(client): assert response.status_code == 200 assert number_of_issues == 1 +def test_api_filters_data(client): + project = f.ProjectFactory.create() + user1 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user1, project=project) + user2 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user2, project=project) + user3 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user3, project=project) + + status0 = f.IssueStatusFactory.create(project=project) + status1 = f.IssueStatusFactory.create(project=project) + status2 = f.IssueStatusFactory.create(project=project) + status3 = f.IssueStatusFactory.create(project=project) + + type1 = f.IssueTypeFactory.create(project=project) + type2 = f.IssueTypeFactory.create(project=project) + + severity0 = f.SeverityFactory.create(project=project) + severity1 = f.SeverityFactory.create(project=project) + severity2 = f.SeverityFactory.create(project=project) + severity3 = f.SeverityFactory.create(project=project) + + priority0 = f.PriorityFactory.create(project=project) + priority1 = f.PriorityFactory.create(project=project) + priority2 = f.PriorityFactory.create(project=project) + priority3 = f.PriorityFactory.create(project=project) + + tag0 = "test1test2test3" + tag1 = "test1" + tag2 = "test2" + tag3 = "test3" + + # ------------------------------------------------------------------------------------------------ + # | Issue | Owner | Assigned To | Status | Type | Priority | Severity | Tags | + # |-------#--------#-------------#---------#-------#-----------#-----------#---------------------| + # | 0 | user2 | None | status3 | type1 | priority2 | severity1 | tag1 | + # | 1 | user1 | None | status3 | type2 | priority2 | severity1 | tag2 | + # | 2 | user3 | None | status1 | type1 | priority3 | severity2 | tag1 tag2 | + # | 3 | user2 | None | status0 | type2 | priority3 | severity1 | tag3 | + # | 4 | user1 | user1 | status0 | type1 | priority2 | severity3 | tag1 tag2 tag3 | + # | 5 | user3 | user1 | status2 | type2 | priority3 | severity2 | tag3 | + # | 6 | user2 | user1 | status3 | type1 | priority2 | severity0 | tag1 tag2 | + # | 7 | user1 | user2 | status0 | type2 | priority1 | severity3 | tag3 | + # | 8 | user3 | user2 | status3 | type1 | priority0 | severity1 | tag1 | + # | 9 | user2 | user3 | status1 | type2 | priority0 | severity2 | tag0 | + # ------------------------------------------------------------------------------------------------ + + issue0 = f.IssueFactory.create(project=project, owner=user2, assigned_to=None, + status=status3, type=type1, priority=priority2, severity=severity1, + tags=[tag1]) + issue1 = f.IssueFactory.create(project=project, owner=user1, assigned_to=None, + status=status3, type=type2, priority=priority2, severity=severity1, + tags=[tag2]) + issue2 = f.IssueFactory.create(project=project, owner=user3, assigned_to=None, + status=status1, type=type1, priority=priority3, severity=severity2, + tags=[tag1, tag2]) + issue3 = f.IssueFactory.create(project=project, owner=user2, assigned_to=None, + status=status0, type=type2, priority=priority3, severity=severity1, + tags=[tag3]) + issue4 = f.IssueFactory.create(project=project, owner=user1, assigned_to=user1, + status=status0, type=type1, priority=priority2, severity=severity3, + tags=[tag1, tag2, tag3]) + issue5 = f.IssueFactory.create(project=project, owner=user3, assigned_to=user1, + status=status2, type=type2, priority=priority3, severity=severity2, + tags=[tag3]) + issue6 = f.IssueFactory.create(project=project, owner=user2, assigned_to=user1, + status=status3, type=type1, priority=priority2, severity=severity0, + tags=[tag1, tag2]) + issue7 = f.IssueFactory.create(project=project, owner=user1, assigned_to=user2, + status=status0, type=type2, priority=priority1, severity=severity3, + tags=[tag3]) + issue8 = f.IssueFactory.create(project=project, owner=user3, assigned_to=user2, + status=status3, type=type1, priority=priority0, severity=severity1, + tags=[tag1]) + issue9 = f.IssueFactory.create(project=project, owner=user2, assigned_to=user3, + status=status1, type=type2, priority=priority0, severity=severity2, + tags=[tag0]) + + url = reverse("issues-filters-data") + "?project={}".format(project.id) + + client.login(user1) + + ## No filter + response = client.get(url) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 3 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 4 + + assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 5 + assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 5 + + assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 3 + + assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 2 + + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 1 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 5 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 4 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 4 + + ## Filter ((status0 or status3) and type1) + response = client.get(url + "&status={},{}&type={}".format(status3.id, status0.id, type1.id)) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 3 + + assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 3 + + assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 1 + + with pytest.raises(StopIteration): + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 4 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1 + + ## Filter ((tag1 and tag2) and (user1 or user2)) + response = client.get(url + "&tags={},{}&owner={},{}".format(tag1, tag2, user1.id, user2.id)) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == type1.id, response.data["types"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == type2.id, response.data["types"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == priority0.id, response.data["priorities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == priority1.id, response.data["priorities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == priority2.id, response.data["priorities"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == priority3.id, response.data["priorities"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == severity0.id, response.data["severities"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == severity1.id, response.data["severities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == severity2.id, response.data["severities"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == severity3.id, response.data["severities"]))["count"] == 1 + + with pytest.raises(StopIteration): + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 2 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1 + def test_get_invalid_csv(client): url = reverse("issues-csv") diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 90b85444..c072b76c 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -244,7 +244,6 @@ def test_filter_by_multiple_status(client): url = reverse("userstories-list") url = "{}?status={},{}".format(reverse("userstories-list"), us1.status.id, us2.status.id) - data = {} response = client.get(url, data) assert len(response.data) == 2 @@ -282,6 +281,137 @@ def test_get_total_points(client): assert us_mixed.get_total_points() == 1.0 +def test_api_filters_data(client): + project = f.ProjectFactory.create() + user1 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user1, project=project) + user2 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user2, project=project) + user3 = f.UserFactory.create(is_superuser=True) + f.MembershipFactory.create(user=user3, project=project) + + status0 = f.UserStoryStatusFactory.create(project=project) + status1 = f.UserStoryStatusFactory.create(project=project) + status2 = f.UserStoryStatusFactory.create(project=project) + status3 = f.UserStoryStatusFactory.create(project=project) + + tag0 = "test1test2test3" + tag1 = "test1" + tag2 = "test2" + tag3 = "test3" + + # ------------------------------------------------------ + # | US | Owner | Assigned To | Tags | + # |-------#--------#-------------#---------------------| + # | 0 | user2 | None | tag1 | + # | 1 | user1 | None | tag2 | + # | 2 | user3 | None | tag1 tag2 | + # | 3 | user2 | None | tag3 | + # | 4 | user1 | user1 | tag1 tag2 tag3 | + # | 5 | user3 | user1 | tag3 | + # | 6 | user2 | user1 | tag1 tag2 | + # | 7 | user1 | user2 | tag3 | + # | 8 | user3 | user2 | tag1 | + # | 9 | user2 | user3 | tag0 | + # ------------------------------------------------------ + + user_story0 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, + status=status3, tags=[tag1]) + user_story1 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=None, + status=status3, tags=[tag2]) + user_story2 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=None, + status=status1, tags=[tag1, tag2]) + user_story3 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=None, + status=status0, tags=[tag3]) + user_story4 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user1, + status=status0, tags=[tag1, tag2, tag3]) + user_story5 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user1, + status=status2, tags=[tag3]) + user_story6 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user1, + status=status3, tags=[tag1, tag2]) + user_story7 = f.UserStoryFactory.create(project=project, owner=user1, assigned_to=user2, + status=status0, tags=[tag3]) + user_story8 = f.UserStoryFactory.create(project=project, owner=user3, assigned_to=user2, + status=status3, tags=[tag1]) + user_story9 = f.UserStoryFactory.create(project=project, owner=user2, assigned_to=user3, + status=status1, tags=[tag0]) + + url = reverse("userstories-filters-data") + "?project={}".format(project.id) + + client.login(user1) + + ## No filter + response = client.get(url) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 3 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 4 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 4 + + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 1 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 5 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 4 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 4 + + ## Filter ((status0 or status3) + response = client.get(url + "&status={},{}".format(status3.id, status0.id)) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 3 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 4 + + with pytest.raises(StopIteration): + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 4 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 3 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 3 + + ## Filter ((tag1 and tag2) and (user1 or user2)) + response = client.get(url + "&tags={},{}&owner={},{}".format(tag1, tag2, user1.id, user2.id)) + assert response.status_code == 200 + + assert next(filter(lambda i: i['id'] == user1.id, response.data["owners"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user2.id, response.data["owners"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == user3.id, response.data["owners"]))["count"] == 1 + + assert next(filter(lambda i: i['id'] == None, response.data["assigned_to"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == user1.id, response.data["assigned_to"]))["count"] == 2 + assert next(filter(lambda i: i['id'] == user2.id, response.data["assigned_to"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == user3.id, response.data["assigned_to"]))["count"] == 0 + + assert next(filter(lambda i: i['id'] == status0.id, response.data["statuses"]))["count"] == 1 + assert next(filter(lambda i: i['id'] == status1.id, response.data["statuses"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == status2.id, response.data["statuses"]))["count"] == 0 + assert next(filter(lambda i: i['id'] == status3.id, response.data["statuses"]))["count"] == 1 + + with pytest.raises(StopIteration): + assert next(filter(lambda i: i['name'] == tag0, response.data["tags"]))["count"] == 0 + assert next(filter(lambda i: i['name'] == tag1, response.data["tags"]))["count"] == 2 + assert next(filter(lambda i: i['name'] == tag2, response.data["tags"]))["count"] == 2 + assert next(filter(lambda i: i['name'] == tag3, response.data["tags"]))["count"] == 1 + + def test_get_invalid_csv(client): url = reverse("userstories-csv") From 9bc0e976ca56d89a395fdcf92e15571673702f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 28 Jul 2015 12:03:19 +0200 Subject: [PATCH 078/190] Refactor users_stats method: use django queryset system --- taiga/stats/services.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/taiga/stats/services.py b/taiga/stats/services.py index 37980d03..3e4fac83 100644 --- a/taiga/stats/services.py +++ b/taiga/stats/services.py @@ -12,10 +12,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from contextlib import closing from django.apps import apps -from django.db import connection +from django.db.models import Count from django.utils import timezone from datetime import timedelta from collections import OrderedDict @@ -37,26 +36,27 @@ def get_users_stats(): # Graph: users last year a_year_ago = timezone.now() - timedelta(days=365) - sql_query = """ - SELECT date_trunc('week', "filtered_users"."date_joined") AS "week", - count(*) - FROM (SELECT * - FROM "users_user" - WHERE "users_user"."is_active" = TRUE - AND "users_user"."is_system" = FALSE - AND "users_user"."date_joined" >= %s) AS "filtered_users" - GROUP BY "week" - ORDER BY "week"; - """ - with closing(connection.cursor()) as cursor: - cursor.execute(sql_query, [a_year_ago]) - rows = cursor.fetchall() + # increments -> + # SELECT date_trunc('week', "filtered_users"."date_joined") AS "week", + # count(*) + # FROM (SELECT * + # FROM "users_user" + # WHERE "users_user"."is_active" = TRUE + # AND "users_user"."is_system" = FALSE + # AND "users_user"."date_joined" >= %s) AS "filtered_users" + # GROUP BY "week" + # ORDER BY "week"; + increments = (queryset.filter(date_joined__gte=a_year_ago) + .extra({"week": "date_trunc('week', date_joined)"}) + .values("week") + .order_by("week") + .annotate(count=Count("id"))) counts_last_year_per_week = OrderedDict() - sumatory = queryset.filter(date_joined__lt=rows[0][0]).count() - for row in rows: - sumatory += row[1] - counts_last_year_per_week[str(row[0].date())] = sumatory + sumatory = queryset.filter(date_joined__lt=increments[0]["week"]).count() + for inc in increments: + sumatory += inc["count"] + counts_last_year_per_week[str(inc["week"].date())] = sumatory stats["counts_last_year_per_week"] = counts_last_year_per_week From 64b636678b90e1b61e9e4d364159c181ee6dc563 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 28 Jul 2015 13:46:13 +0200 Subject: [PATCH 079/190] Adding neighbors info only in GET requests to API --- taiga/projects/issues/api.py | 11 +++++++---- taiga/projects/tasks/api.py | 10 +++++++--- taiga/projects/userstories/api.py | 10 +++++++--- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index bb377e8d..7a1033b6 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -43,10 +43,7 @@ from . import serializers class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): - serializer_class = serializers.IssueNeighborsSerializer - list_serializer_class = serializers.IssueSerializer permission_classes = (permissions.IssuePermission, ) - filter_backends = (filters.CanViewIssuesFilterBackend, filters.OwnersFilter, filters.AssignedToFilter, @@ -78,6 +75,12 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, "assigned_to", "subject") + def get_serializer_class(self, *args, **kwargs): + if self.action in ["retrieve", "by_ref"]: + return serializers.IssueNeighborsSerializer + + return serializers.IssueSerializer + def update(self, request, *args, **kwargs): self.object = self.get_object_or_none() project_id = request.DATA.get('project', None) @@ -225,7 +228,7 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, status=project.default_issue_status, severity=project.default_severity, priority=project.default_priority, type=project.default_issue_type, callback=self.post_save, precall=self.pre_save) - issues_serialized = self.serializer_class(issues, many=True) + issues_serialized = self.get_serializer_class()(issues, many=True) return response.Ok(data=issues_serialized.data) diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 5d5581cd..7b6b90ac 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -37,13 +37,17 @@ from . import services class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): model = models.Task - serializer_class = serializers.TaskNeighborsSerializer - list_serializer_class = serializers.TaskSerializer permission_classes = (permissions.TaskPermission,) filter_backends = (filters.CanViewTasksFilterBackend,) filter_fields = ["user_story", "milestone", "project", "assigned_to", "status__is_closed", "watchers"] + def get_serializer_class(self, *args, **kwargs): + if self.action in ["retrieve", "by_ref"]: + return serializers.TaskNeighborsSerializer + + return serializers.TaskSerializer + def update(self, request, *args, **kwargs): self.object = self.get_object_or_none() project_id = request.DATA.get('project', None) @@ -129,7 +133,7 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, data["bulk_tasks"], milestone_id=data["sprint_id"], user_story_id=data["us_id"], status_id=data.get("status_id") or project.default_task_status_id, project=project, owner=request.user, callback=self.post_save, precall=self.pre_save) - tasks_serialized = self.serializer_class(tasks, many=True) + tasks_serialized = self.get_serializer_class()(tasks, many=True) return response.Ok(tasks_serialized.data) diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 0467ef16..e3d7f986 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -46,8 +46,6 @@ from . import services class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): model = models.UserStory - serializer_class = serializers.UserStoryNeighborsSerializer - list_serializer_class = serializers.UserStorySerializer permission_classes = (permissions.UserStoryPermission,) filter_backends = (filters.CanViewUsFilterBackend, filters.OwnersFilter, @@ -74,6 +72,12 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi # Specific filter used for filtering neighbor user stories _neighbor_tags_filter = filters.TagsFilter('neighbor_tags') + def get_serializer_class(self, *args, **kwargs): + if self.action in ["retrieve", "by_ref"]: + return serializers.UserStoryNeighborsSerializer + + return serializers.UserStorySerializer + def update(self, request, *args, **kwargs): self.object = self.get_object_or_none() project_id = request.DATA.get('project', None) @@ -201,7 +205,7 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi data["bulk_stories"], project=project, owner=request.user, status_id=data.get("status_id") or project.default_us_status_id, callback=self.post_save, precall=self.pre_save) - user_stories_serialized = self.serializer_class(user_stories, many=True) + user_stories_serialized = self.get_serializer_class()(user_stories, many=True) return response.Ok(user_stories_serialized.data) return response.BadRequest(serializer.errors) From d750a1dc9066bd2b437777f1a7099443a2cb793d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 29 Jul 2015 14:50:40 +0200 Subject: [PATCH 080/190] Fixing signals disconnection --- taiga/projects/api.py | 18 +++++++-------- taiga/projects/apps.py | 16 ++++++------- taiga/projects/issues/apps.py | 29 +++++++++++++++++------ taiga/projects/tasks/apps.py | 33 ++++++++++++++++++-------- taiga/projects/userstories/apps.py | 37 +++++++++++++++++++++--------- taiga/webhooks/apps.py | 2 +- 6 files changed, 90 insertions(+), 45 deletions(-) diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 94941cb8..cd70fbf3 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -250,9 +250,9 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): def destroy(self, request, *args, **kwargs): from taiga.events.apps import connect_events_signals, disconnect_events_signals - from taiga.projects.tasks.apps import connect_tasks_signals, disconnect_tasks_signals - from taiga.projects.userstories.apps import connect_userstories_signals, disconnect_userstories_signals - from taiga.projects.issues.apps import connect_issues_signals, disconnect_issues_signals + from taiga.projects.tasks.apps import connect_all_tasks_signals, disconnect_all_tasks_signals + from taiga.projects.userstories.apps import connect_all_userstories_signals, disconnect_all_userstories_signals + from taiga.projects.issues.apps import connect_all_issues_signals, disconnect_all_issues_signals from taiga.projects.apps import connect_memberships_signals, disconnect_memberships_signals obj = self.get_object_or_none() @@ -262,9 +262,9 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): raise Http404 disconnect_events_signals() - disconnect_issues_signals() - disconnect_tasks_signals() - disconnect_userstories_signals() + disconnect_all_issues_signals() + disconnect_all_tasks_signals() + disconnect_all_userstories_signals() disconnect_memberships_signals() try: @@ -275,9 +275,9 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): obj.roles.all().delete() finally: connect_events_signals() - connect_issues_signals() - connect_tasks_signals() - connect_userstories_signals() + connect_all_issues_signals() + connect_all_tasks_signals() + connect_all_userstories_signals() connect_memberships_signals() self.pre_delete(obj) diff --git a/taiga/projects/apps.py b/taiga/projects/apps.py index c3ef4df3..acdaa7da 100644 --- a/taiga/projects/apps.py +++ b/taiga/projects/apps.py @@ -66,21 +66,21 @@ def connect_task_status_signals(): def disconnect_memberships_signals(): - signals.pre_delete.disconnect(dispatch_uid='membership_pre_delete') - signals.post_delete.disconnect(dispatch_uid='update_watchers_on_membership_post_delete') - signals.post_save.disconnect(dispatch_uid='create-notify-policy') + signals.pre_delete.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='membership_pre_delete') + signals.post_delete.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='update_watchers_on_membership_post_delete') + signals.post_save.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='create-notify-policy') def disconnect_projects_signals(): - signals.post_save.disconnect(dispatch_uid='project_post_save') - signals.pre_save.disconnect(dispatch_uid="tags_normalization_projects") - signals.pre_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") + signals.post_save.disconnect(sender=apps.get_model("projects", "Project"), dispatch_uid='project_post_save') + signals.pre_save.disconnect(sender=apps.get_model("projects", "Project"), dispatch_uid="tags_normalization_projects") + signals.pre_save.disconnect(sender=apps.get_model("projects", "Project"), dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_projects") def disconnect_us_status_signals(): - signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_user_stories_when_edit_us_status") + signals.post_save.disconnect(sender=apps.get_model("projects", "UserStoryStatus"), dispatch_uid="try_to_close_or_open_user_stories_when_edit_us_status") def disconnect_task_status_signals(): - signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_user_stories_when_edit_task_status") + signals.post_save.disconnect(sender=apps.get_model("projects", "TaskStatus"), dispatch_uid="try_to_close_or_open_user_stories_when_edit_task_status") class ProjectsAppConfig(AppConfig): diff --git a/taiga/projects/issues/apps.py b/taiga/projects/issues/apps.py index 7333d934..cc68fb73 100644 --- a/taiga/projects/issues/apps.py +++ b/taiga/projects/issues/apps.py @@ -40,17 +40,32 @@ def connect_issues_signals(): sender=apps.get_model("issues", "Issue"), dispatch_uid="update_project_tags_when_delete_taggable_item_issue") - # Custom Attributes + +def connect_issues_custom_attributes_signals(): signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_issue, sender=apps.get_model("issues", "Issue"), dispatch_uid="create_custom_attribute_value_when_create_issue") + +def connect_all_issues_signals(): + connect_issues_signals() + connect_issues_custom_attributes_signals() + + def disconnect_issues_signals(): - signals.pre_save.disconnect(dispatch_uid="set_finished_date_when_edit_issue") - signals.pre_save.disconnect(dispatch_uid="tags_normalization_issue") - signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_issue") - signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_taggable_item_issue") - signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_issue") + signals.pre_save.disconnect(sender=apps.get_model("issues", "Issue"), dispatch_uid="set_finished_date_when_edit_issue") + signals.pre_save.disconnect(sender=apps.get_model("issues", "Issue"), dispatch_uid="tags_normalization_issue") + signals.post_save.disconnect(sender=apps.get_model("issues", "Issue"), dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_issue") + signals.post_delete.disconnect(sender=apps.get_model("issues", "Issue"), dispatch_uid="update_project_tags_when_delete_taggable_item_issue") + + +def disconnect_issues_custom_attributes_signals(): + signals.post_save.disconnect(sender=apps.get_model("issues", "Issue"), dispatch_uid="create_custom_attribute_value_when_create_issue") + + +def disconnect_all_issues_signals(): + disconnect_issues_signals() + disconnect_issues_custom_attributes_signals() class IssuesAppConfig(AppConfig): @@ -58,4 +73,4 @@ class IssuesAppConfig(AppConfig): verbose_name = "Issues" def ready(self): - connect_issues_signals() + connect_all_issues_signals() diff --git a/taiga/projects/tasks/apps.py b/taiga/projects/tasks/apps.py index f8426dcc..445ca63b 100644 --- a/taiga/projects/tasks/apps.py +++ b/taiga/projects/tasks/apps.py @@ -47,19 +47,34 @@ def connect_tasks_signals(): sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_delete_tagglabe_item_task") - # Custom Attributes + +def connect_tasks_custom_attributes_signals(): signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_task, sender=apps.get_model("tasks", "Task"), dispatch_uid="create_custom_attribute_value_when_create_task") + +def connect_all_tasks_signals(): + connect_tasks_signals() + connect_tasks_custom_attributes_signals() + + def disconnect_tasks_signals(): - signals.pre_save.disconnect(dispatch_uid="cached_prev_task") - signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") - signals.post_delete.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") - signals.pre_save.disconnect(dispatch_uid="tags_normalization") - signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_tagglabe_item") - signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_tagglabe_item") - signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_task") + signals.pre_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="cached_prev_task") + signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") + signals.post_delete.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") + signals.pre_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="tags_normalization") + signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_create_or_edit_tagglabe_item") + signals.post_delete.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_delete_tagglabe_item") + + +def disconnect_tasks_custom_attributes_signals(): + signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="create_custom_attribute_value_when_create_task") + + +def disconnect_all_tasks_signals(): + disconnect_tasks_signals() + disconnect_tasks_custom_attributes_signals() class TasksAppConfig(AppConfig): @@ -67,4 +82,4 @@ class TasksAppConfig(AppConfig): verbose_name = "Tasks" def ready(self): - connect_tasks_signals() + connect_all_tasks_signals() diff --git a/taiga/projects/userstories/apps.py b/taiga/projects/userstories/apps.py index 868074c1..948e7c08 100644 --- a/taiga/projects/userstories/apps.py +++ b/taiga/projects/userstories/apps.py @@ -58,21 +58,36 @@ def connect_userstories_signals(): sender=apps.get_model("userstories", "UserStory"), dispatch_uid="update_project_tags_when_delete_taggable_item_user_story") - # Custom Attributes + +def connect_userstories_custom_attributes_signals(): signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_user_story, sender=apps.get_model("userstories", "UserStory"), dispatch_uid="create_custom_attribute_value_when_create_user_story") + +def connect_all_userstories_signals(): + connect_userstories_signals() + connect_userstories_custom_attributes_signals() + + def disconnect_userstories_signals(): - signals.pre_save.disconnect(dispatch_uid="cached_prev_us") - signals.post_save.disconnect(dispatch_uid="update_role_points_when_create_or_edit_us") - signals.post_save.disconnect(dispatch_uid="update_milestone_of_tasks_when_edit_us") - signals.post_save.disconnect(dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_us") - signals.post_delete.disconnect(dispatch_uid="try_to_close_milestone_when_delete_us") - signals.pre_save.disconnect(dispatch_uid="tags_normalization_user_story") - signals.post_save.disconnect(dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_user_story") - signals.post_delete.disconnect(dispatch_uid="update_project_tags_when_delete_taggable_item_user_story") - signals.post_save.disconnect(dispatch_uid="create_custom_attribute_value_when_create_user_story") + signals.pre_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="cached_prev_us") + signals.post_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="update_role_points_when_create_or_edit_us") + signals.post_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="update_milestone_of_tasks_when_edit_us") + signals.post_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_us") + signals.post_delete.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="try_to_close_milestone_when_delete_us") + signals.pre_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="tags_normalization_user_story") + signals.post_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="update_project_tags_when_create_or_edit_taggable_item_user_story") + signals.post_delete.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="update_project_tags_when_delete_taggable_item_user_story") + + +def disconnect_userstories_custom_attributes_signals(): + signals.post_save.disconnect(sender=apps.get_model("userstories", "UserStory"), dispatch_uid="create_custom_attribute_value_when_create_user_story") + + +def disconnect_all_userstories_signals(): + disconnect_userstories_signals() + disconnect_userstories_custom_attributes_signals() class UserStoriesAppConfig(AppConfig): @@ -80,4 +95,4 @@ class UserStoriesAppConfig(AppConfig): verbose_name = "User Stories" def ready(self): - connect_userstories_signals() + connect_all_userstories_signals() diff --git a/taiga/webhooks/apps.py b/taiga/webhooks/apps.py index 5ae2ac20..f0444d68 100644 --- a/taiga/webhooks/apps.py +++ b/taiga/webhooks/apps.py @@ -26,7 +26,7 @@ def connect_webhooks_signals(): def disconnect_webhooks_signals(): - signals.post_save.disconnect(dispatch_uid="webhooks") + signals.post_save.disconnect(sender=HistoryEntry, dispatch_uid="webhooks") class WebhooksAppConfig(AppConfig): From f5f575267d83bc751e2863b3cfc346d6119d6cf3 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 30 Jul 2015 14:31:26 +0200 Subject: [PATCH 081/190] Disabling most signals on bulk creation --- taiga/projects/issues/services.py | 12 +++++++++++- taiga/projects/tasks/services.py | 12 +++++++++++- taiga/projects/userstories/services.py | 13 ++++++++++++- .../resources_permissions/test_tasks_resources.py | 4 ++-- .../test_userstories_resources.py | 4 ++-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/taiga/projects/issues/services.py b/taiga/projects/issues/services.py index 7d6f3061..9a553cd3 100644 --- a/taiga/projects/issues/services.py +++ b/taiga/projects/issues/services.py @@ -24,6 +24,9 @@ from django.db import connection from django.utils.translation import ugettext as _ from taiga.base.utils import db, text +from taiga.projects.issues.apps import ( + connect_issues_signals, + disconnect_issues_signals) from . import models @@ -50,7 +53,14 @@ def create_issues_in_bulk(bulk_data, callback=None, precall=None, **additional_f :return: List of created `Issue` instances. """ issues = get_issues_from_bulk(bulk_data, **additional_fields) - db.save_in_bulk(issues, callback, precall) + + disconnect_issues_signals() + + try: + db.save_in_bulk(issues, callback, precall) + finally: + connect_issues_signals() + return issues diff --git a/taiga/projects/tasks/services.py b/taiga/projects/tasks/services.py index 89fec76c..61225aff 100644 --- a/taiga/projects/tasks/services.py +++ b/taiga/projects/tasks/services.py @@ -19,6 +19,9 @@ import csv from taiga.base.utils import db, text from taiga.projects.history.services import take_snapshot +from taiga.projects.tasks.apps import ( + connect_tasks_signals, + disconnect_tasks_signals) from taiga.events import events from . import models @@ -46,7 +49,14 @@ def create_tasks_in_bulk(bulk_data, callback=None, precall=None, **additional_fi :return: List of created `Task` instances. """ tasks = get_tasks_from_bulk(bulk_data, **additional_fields) - db.save_in_bulk(tasks, callback, precall) + + disconnect_tasks_signals() + + try: + db.save_in_bulk(tasks, callback, precall) + finally: + connect_tasks_signals() + return tasks diff --git a/taiga/projects/userstories/services.py b/taiga/projects/userstories/services.py index 9de3bf53..9d913707 100644 --- a/taiga/projects/userstories/services.py +++ b/taiga/projects/userstories/services.py @@ -26,6 +26,10 @@ from django.utils.translation import ugettext as _ from taiga.base.utils import db, text from taiga.projects.history.services import take_snapshot +from taiga.projects.userstories.apps import ( + connect_userstories_signals, + disconnect_userstories_signals) + from taiga.events import events from . import models @@ -53,7 +57,14 @@ def create_userstories_in_bulk(bulk_data, callback=None, precall=None, **additio :return: List of created `Task` instances. """ userstories = get_userstories_from_bulk(bulk_data, **additional_fields) - db.save_in_bulk(userstories, callback, precall) + + disconnect_userstories_signals() + + try: + db.save_in_bulk(userstories, callback, precall) + finally: + connect_userstories_signals() + return userstories diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index 2259cfa4..9be43c09 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -16,11 +16,11 @@ import pytest pytestmark = pytest.mark.django_db -def setup_module(module): +def setup_function(function): disconnect_signals() -def teardown_module(module): +def setup_function(function): reconnect_signals() diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index a3e09808..f41d84ee 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -16,11 +16,11 @@ import pytest pytestmark = pytest.mark.django_db -def setup_module(module): +def setup_function(function): disconnect_signals() -def teardown_module(module): +def teardown_function(function): reconnect_signals() From 66a3398c33e490200b94bf06f439962a91a83e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 31 Jul 2015 09:20:51 +0200 Subject: [PATCH 082/190] [118n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 3 +- taiga/locale/de/LC_MESSAGES/django.po | 13 +++- taiga/locale/es/LC_MESSAGES/django.po | 3 +- taiga/locale/fi/LC_MESSAGES/django.po | 3 +- taiga/locale/fr/LC_MESSAGES/django.po | 90 ++++++++++++++++++++-- taiga/locale/nl/LC_MESSAGES/django.po | 3 +- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 4 +- 7 files changed, 102 insertions(+), 17 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index d1fe3a04..8ed89dce 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -12,7 +12,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Catalan (http://www.transifex.com/p/taiga-back/language/ca/)\n" +"Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" +"language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index bc0f2ec2..d54088e0 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -14,9 +14,10 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-07-06 16:06+0200\n" -"PO-Revision-Date: 2015-07-09 10:40+0000\n" +"PO-Revision-Date: 2015-07-21 08:47+0000\n" "Last-Translator: Regina \n" -"Language-Team: German (http://www.transifex.com/p/taiga-back/language/de/)\n" +"Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" +"language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -965,7 +966,7 @@ msgstr "" #: taiga/hooks/bitbucket/event_hooks.py:142 msgid "Issue created from BitBucket." -msgstr "" +msgstr "Ticket erstellt von BitBucket." #: taiga/hooks/bitbucket/event_hooks.py:166 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 @@ -991,6 +992,9 @@ msgid "" "\n" "{message}" msgstr "" +"Kommentar von BitBucket\n" +"\n" +"{message}" #: taiga/hooks/github/event_hooks.py:96 #, python-brace-format @@ -1082,6 +1086,9 @@ msgid "" "\n" "{message}" msgstr "" +"Kommentar von GitLab:\n" +"\n" +"{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 73bac6ef..1a86b943 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -15,7 +15,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:16+0000\n" "Last-Translator: David Barragán \n" -"Language-Team: Spanish (http://www.transifex.com/p/taiga-back/language/es/)\n" +"Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" +"language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index dbd2a3b3..d2c0033a 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -12,7 +12,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Finnish (http://www.transifex.com/p/taiga-back/language/fi/)\n" +"Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" +"language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index cc4ee377..0cb822b2 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -5,6 +5,7 @@ # Translators: # Alain Poirier , 2015 # David Barragán , 2015 +# Djyp Forest Fortin , 2015 # Florent B. , 2015 # Louis-Michel Couture , 2015 # Matthieu Durocher , 2015 @@ -16,9 +17,10 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-07-06 16:06+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" -"Last-Translator: Taiga Dev Team \n" -"Language-Team: French (http://www.transifex.com/p/taiga-back/language/fr/)\n" +"PO-Revision-Date: 2015-07-21 06:58+0000\n" +"Last-Translator: Djyp Forest Fortin \n" +"Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" +"language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -430,6 +432,26 @@ msgid "" " \n" " " msgstr "" +"\n" +" Support de " +"Taiga :\n" +" %(support_url)s\n" +"
\n" +" Nous contacter :" +"\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Groupe de " +"discussion :\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " #: taiga/base/templates/emails/hero-body-html.jinja:6 msgid "You have been Taigatized" @@ -597,6 +619,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Project dump generated

\n" +"

Bonjour %(user)s,

\n" +"

Votre dump du projet %(project)s a bie nété généré.

\n" +"

Vous pouvez le télécharger ici :

\n" +" Télécharger le dump\n" +"

Ce fichier sera supprimé le %(deletion_date)s.

\n" +"

L'équipe Taiga

\n" +" " #: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 #, python-format @@ -614,6 +646,18 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Bonjour %(user)s,\n" +"\n" +"Votre dump du projet %(project)s a bien été créé. Vous pouvez le télécharger " +"ici :\n" +"\n" +"%(url)s\n" +"\n" +"Ce fichier sera supprimé le %(deletion_date)s.\n" +"\n" +"---\n" +"L'équipe Taiga\n" #: taiga/export_import/templates/emails/dump_project-subject.jinja:1 #, python-format @@ -634,6 +678,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

%(error_message)s

\n" +"

Bonjour %(user)s,

\n" +"

Votre projet %(project)s n'a pas été exporté correctement.

\n" +"

L'administrateur système de Taiga en a été informé.
Merci " +"d'essayer à nouveau ou de contacter le support à\n" +" %(support_email)s

\n" +"

L'équipe Taiga

\n" +" " #: taiga/export_import/templates/emails/export_error-body-text.jinja:1 #, python-format @@ -651,6 +705,18 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Bonjour %(user)s,\n" +"\n" +"%(error_message)s\n" +"Votre projet %(project)s n'a pas été exporté correctement.\n" +"\n" +"L'administrateur système de Taiga en a été informé.\n" +"\n" +"Merci d'essayer à nouveau ou de contacter le support à %(support_email)s\n" +"\n" +"---\n" +"L'équipe Taiga\n" #: taiga/export_import/templates/emails/export_error-subject.jinja:1 #, python-format @@ -1227,11 +1293,11 @@ msgstr "Talky" #: taiga/projects/custom_attributes/models.py:33 msgid "Text" -msgstr "" +msgstr "Texte" #: taiga/projects/custom_attributes/models.py:34 msgid "Multi-Line Text" -msgstr "" +msgstr "Texte multi-ligne" #: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 @@ -1400,7 +1466,7 @@ msgstr "note bloquée" #: taiga/projects/history/templatetags/functions.py:28 msgid "sprint" -msgstr "" +msgstr "sprint" #: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." @@ -1527,7 +1593,7 @@ msgstr "Text supplémentaire de l'invitation" #: taiga/projects/models.py:72 msgid "user order" -msgstr "" +msgstr "classement utilisateur" #: taiga/projects/models.py:78 msgid "The user is already member of the project" @@ -2421,6 +2487,14 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Vous avez été ajouté-e à un projet

\n" +"

Bonjour %(full_name)s,
vous avez été ajouté-e au projet " +"%(project)s

\n" +" Aller " +"au projet\n" +"

L'équipe de Taiga

\n" +" " #: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 #, python-format @@ -2899,7 +2973,7 @@ msgstr "langage par défaut" #: taiga/users/models.py:122 msgid "default theme" -msgstr "" +msgstr "thème par défaut" #: taiga/users/models.py:124 msgid "default timezone" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index b783b143..b37c7ec8 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -12,7 +12,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Dutch (http://www.transifex.com/p/taiga-back/language/nl/)\n" +"Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" +"language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 677e57d3..b69c9de7 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -14,8 +14,8 @@ msgstr "" "POT-Creation-Date: 2015-07-06 16:06+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Chinese Traditional (http://www.transifex.com/p/taiga-back/" -"language/zh-Hant/)\n" +"Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" +"taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" From f6f3a5a0af19cdcc791d53eb5f860027692822cc Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 30 Jul 2015 12:53:08 +0200 Subject: [PATCH 083/190] Adding deleted datetime to webhook sent info when deleting elements --- CHANGELOG.md | 1 + taiga/webhooks/signal_handlers.py | 3 ++- taiga/webhooks/tasks.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae3a7e8..076710fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) - API: Improve and fix some errors in issues/filters_data and userstories/filters_data. +- Webhooks: Add deleted datetime to webhooks responses when isues, tasks or USs are deleted. - Lots of small and not so small bugfixes. diff --git a/taiga/webhooks/signal_handlers.py b/taiga/webhooks/signal_handlers.py index 0483b145..6d31e2bd 100644 --- a/taiga/webhooks/signal_handlers.py +++ b/taiga/webhooks/signal_handlers.py @@ -15,6 +15,7 @@ # along with this program. If not, see . from django.conf import settings +from django.utils import timezone from taiga.projects.history import services as history_service from taiga.projects.history.choices import HistoryType @@ -54,7 +55,7 @@ def on_new_history_entry(sender, instance, created, **kwargs): extra_args = [instance] elif instance.type == HistoryType.delete: task = tasks.delete_webhook - extra_args = [] + extra_args = [timezone.now()] for webhook in webhooks: args = [webhook["id"], webhook["url"], webhook["key"], obj] + extra_args diff --git a/taiga/webhooks/tasks.py b/taiga/webhooks/tasks.py index 679da3b8..d905167f 100644 --- a/taiga/webhooks/tasks.py +++ b/taiga/webhooks/tasks.py @@ -112,11 +112,12 @@ def create_webhook(webhook_id, url, key, obj): @app.task -def delete_webhook(webhook_id, url, key, obj): +def delete_webhook(webhook_id, url, key, obj, deleted_date): data = {} data['data'] = _serialize(obj) data['action'] = "delete" data['type'] = _get_type(obj) + data['deleted_date'] = deleted_date return _send_request(webhook_id, url, key, data) From d9028cfbedfd660f0de96ccec07679511e13f07a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sun, 2 Aug 2015 10:38:35 +0200 Subject: [PATCH 084/190] [i18n] Update locles --- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 50 ++++++++++++++++------ 1 file changed, 37 insertions(+), 13 deletions(-) diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index b69c9de7..7028e182 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-07-06 16:06+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-08-01 03:51+0000\n" +"Last-Translator: Chi-Hsun Tsai \n" "Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" @@ -933,10 +933,16 @@ msgid "" "\n" "{description}" msgstr "" +"來自[@{bitbucket_user_name}]({bitbucket_user_url} 的問題\"詳見 " +"@{bitbucket_user_name}'s BitBucket profile\") BitBucket.\n" +"源自BitBucket 問題: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" #: taiga/hooks/bitbucket/event_hooks.py:142 msgid "Issue created from BitBucket." -msgstr "" +msgstr "來自BitBucket的問題:" #: taiga/hooks/bitbucket/event_hooks.py:166 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 @@ -954,6 +960,12 @@ msgid "" "\n" "{message}" msgstr "" +" [@{bitbucket_user_name}]({bitbucket_user_url}之評論 \"參見 " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"源自BitBucket 問題: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" #: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format @@ -962,6 +974,9 @@ msgid "" "\n" "{message}" msgstr "" +"來自BitBucket的評論:\n" +"\n" +"{message}" #: taiga/hooks/github/event_hooks.py:96 #, python-brace-format @@ -1042,6 +1057,12 @@ msgid "" "\n" "{message}" msgstr "" +" [@{gitlab_user_name}]({gitlab_user_url}之評論 \"參見 @{gitlab_user_name}'s " +"GitLab profile\") from GitLab.\n" +"源自 GitLab 問題: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" #: taiga/hooks/gitlab/event_hooks.py:171 #, python-brace-format @@ -1050,6 +1071,9 @@ msgid "" "\n" "{message}" msgstr "" +"來自GitLab的評論:\n" +"\n" +"{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:52 @@ -1312,7 +1336,7 @@ msgstr "Jitsi" #: taiga/projects/choices.py:23 msgid "Custom" -msgstr "" +msgstr "自定" #: taiga/projects/choices.py:24 msgid "Talky" @@ -1320,11 +1344,11 @@ msgstr "Talky" #: taiga/projects/custom_attributes/models.py:33 msgid "Text" -msgstr "" +msgstr "單行文字" #: taiga/projects/custom_attributes/models.py:34 msgid "Multi-Line Text" -msgstr "" +msgstr "多行列文字" #: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 @@ -1493,7 +1517,7 @@ msgstr "封鎖筆記" #: taiga/projects/history/templatetags/functions.py:28 msgid "sprint" -msgstr "" +msgstr "衝刺任務" #: taiga/projects/issues/api.py:195 msgid "You don't have permissions to set this sprint to this issue." @@ -1688,7 +1712,7 @@ msgstr "視訊會議系統" #: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference extra data" -msgstr "" +msgstr "視訊會議額外資料" #: taiga/projects/models.py:159 msgid "creation template" @@ -2614,15 +2638,15 @@ msgstr "專案結束" #: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 msgid "You don't have permissions to set this sprint to this task." -msgstr "" +msgstr "無權限更動此任務下的衝刺任務" #: taiga/projects/tasks/api.py:93 msgid "You don't have permissions to set this user story to this task." -msgstr "" +msgstr "無權限更動此務下的使用者故事" #: taiga/projects/tasks/api.py:96 msgid "You don't have permissions to set this status to this task." -msgstr "" +msgstr "無權限更動此任務下的狀態" #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -3024,11 +3048,11 @@ msgstr "利害關係人" #: taiga/projects/userstories/api.py:134 msgid "You don't have permissions to set this sprint to this user story." -msgstr "" +msgstr "無權限更動使用者故事的衝刺任務" #: taiga/projects/userstories/api.py:138 msgid "You don't have permissions to set this status to this user story." -msgstr "" +msgstr "無權限更動此使用者故事的狀態" #: taiga/projects/userstories/api.py:212 #, python-brace-format From ade62970825bf083fb9ac6880f82074c02950d76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 3 Aug 2015 11:12:38 +0200 Subject: [PATCH 085/190] [i18n] Add ru --- CHANGELOG.md | 1 + settings/common.py | 2 +- taiga/locale/ca/LC_MESSAGES/django.po | 42 +- taiga/locale/de/LC_MESSAGES/django.po | 42 +- taiga/locale/en/LC_MESSAGES/django.po | 42 +- taiga/locale/es/LC_MESSAGES/django.po | 42 +- taiga/locale/fi/LC_MESSAGES/django.po | 42 +- taiga/locale/fr/LC_MESSAGES/django.po | 42 +- taiga/locale/nl/LC_MESSAGES/django.po | 42 +- taiga/locale/pl/LC_MESSAGES/django.po | 42 +- taiga/locale/ru/LC_MESSAGES/django.po | 3476 ++++++++++++++++++++ taiga/locale/zh-Hant/LC_MESSAGES/django.po | 42 +- 12 files changed, 3667 insertions(+), 190 deletions(-) create mode 100644 taiga/locale/ru/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index 076710fa..ea7f3ce6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. - i18n. - Add polish (pl) translation. + - Add russian (ru) translation. ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. diff --git a/settings/common.py b/settings/common.py index 1799c4b7..26ee9a51 100644 --- a/settings/common.py +++ b/settings/common.py @@ -130,7 +130,7 @@ LANGUAGES = [ #("pt", "Português (Portugal)"), # Portuguese #("pt-br", "Português (Brasil)"), # Brazilian Portuguese #("ro", "Română"), # Romanian - #("ru", "Русский"), # Russian + ("ru", "Русский"), # Russian #("sk", "Slovenčina"), # Slovak #("sl", "Slovenščina"), # Slovenian #("sq", "Shqip"), # Albanian diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 8ed89dce..acebafc2 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" @@ -329,12 +329,12 @@ msgstr "Error d'integritat per argument invàlid o erroni." msgid "Precondition error" msgstr "Precondició errònia." -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "" -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "" @@ -783,8 +783,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no és un arxiu json vàlid" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "El projecte no existeix" @@ -1086,19 +1086,19 @@ msgstr "Administrar valors de projecte" msgid "Admin roles" msgstr "Administrar rols" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Al menys un del usuaris ha de ser administrador" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." @@ -1368,23 +1368,23 @@ msgstr "nota de bloqueig" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tens permissos per a ficar aquest sprint a aquesta incidència" -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "No tens permissos per a ficar aquest status a aquesta tasca" -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "No tens permissos per a ficar aquesta severitat a aquesta tasca" -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "No tens permissos per a ficar aquesta prioritat a aquesta incidència" -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "No tens permissos per a ficar aquest tipus a aquesta incidència" @@ -2241,15 +2241,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2627,15 +2627,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index d54088e0..48eadfb2 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-21 08:47+0000\n" "Last-Translator: Regina \n" "Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" @@ -355,12 +355,12 @@ msgstr "Integritätsfehler wegen falscher oder ungültiger Argumente" msgid "Precondition error" msgstr "Voraussetzungsfehler" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Fehler in Filter Parameter Typen." -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' muss ein Integer-Wert sein." @@ -925,8 +925,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Die Nutzlast ist kein gültiges json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "Das Projekt existiert nicht" @@ -1252,19 +1252,19 @@ msgstr "Administrator Projekt Werte" msgid "Admin roles" msgstr "Administrator-Rollen" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Unglültiger Templatename" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Ungültige Templatebeschreibung" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Mindestens ein Benutzer muss ein aktiver Administrator sein. " -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" @@ -1534,27 +1534,27 @@ msgstr "Blockierungsgrund" msgid "sprint" msgstr "Sprint" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Sprint zu setzen." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Status zu setzen. " -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Gewichtung zu setzen." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Priorität zu setzen. " -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "Sie haben nicht die Berechtigung, das Ticket auf diese Art zu setzen." @@ -2691,18 +2691,18 @@ msgstr "Zukünftiger Sprint" msgid "Project End" msgstr "Projektende" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" "Sie haben nicht die Berechtigung, diesen Sprint auf diese Aufgabe zu setzen" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" "Sie haben nicht die Berechtigung, diese User-Story auf diese Aufgabe zu " "setzen" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -3097,15 +3097,15 @@ msgstr "Projekteigentümer " msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 5c250211..b05f8874 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -321,12 +321,12 @@ msgstr "" msgid "Precondition error" msgstr "" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "" -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "" @@ -756,8 +756,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "" @@ -1059,19 +1059,19 @@ msgstr "" msgid "Admin roles" msgstr "" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "" @@ -1341,23 +1341,23 @@ msgstr "" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "" -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "" -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "" -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "" -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "" @@ -2208,15 +2208,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2576,15 +2576,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 1a86b943..ce90b2a1 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-06 14:16+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" @@ -345,12 +345,12 @@ msgstr "Error de integridad por argumentos incorrectos o inválidos" msgid "Precondition error" msgstr "Error por incumplimiento de precondición" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Error en los típos de parámetros de filtrado" -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' debe ser un valor entero." @@ -908,8 +908,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no es un json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "El proyecto no existe" @@ -1252,19 +1252,19 @@ msgstr "Administrar valores de proyecto" msgid "Admin roles" msgstr "Administrar roles" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Nombre de plantilla invalido" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Descripción de plantilla invalida" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Al menos uno de los usuario debe ser un administrador." -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." @@ -1534,23 +1534,23 @@ msgstr "nota de bloqueo" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tienes permisos para asignar un sprint a esta petición." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "No tienes permisos para asignar un estado a esta petición." -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "No tienes permisos para establecer la gravedad de esta petición." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "No tienes permiso para establecer la prioridad de esta petición." -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "No tienes permiso para establecer el tipo de esta petición." @@ -2641,15 +2641,15 @@ msgstr "Sprint futuro" msgid "Project End" msgstr "Final de proyecto" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "No tienes permisos para asignar este sprint a esta tarea." -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "No tienes permisos para asignar esta historia a esta tarea." -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "No tienes permisos para asignar este estado a esta tarea." @@ -3061,17 +3061,17 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "No tienes permisos para asignar este sprint a esta historia de usuario." -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" "No tienes permisos para asignar este estado a esta historia de usuario." -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index d2c0033a..be5f7ffb 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" @@ -331,12 +331,12 @@ msgstr "Integrity Error for wrong or invalid arguments" msgid "Precondition error" msgstr "Precondition error" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Error in filter params types." -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' must be an integer value." @@ -894,8 +894,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "The payload is not a valid json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "Projektia ei löydy" @@ -1215,19 +1215,19 @@ msgstr "Hallinnoi projektin arvoja" msgid "Admin roles" msgstr "Hallinnoi rooleja" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Virheellinen mallipohjan nimi" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Virheellinen mallipohjan kuvaus" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Vähintään yhden käyttäjän pitää olla aktiivinen ylläpitäjä" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." @@ -1497,23 +1497,23 @@ msgstr "suljettu muistiinpano" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "Sinulla ei ole oikeuksia laittaa kierrosta tälle pyynnölle." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "Sinulla ei ole oikeutta asettaa statusta tälle pyyntö." -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "Sinulla ei ole oikeutta asettaa vakavuutta tälle pyynnölle." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "Sinulla ei ole oikeutta asettaa kiireellisyyttä tälle pyynnölle." -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "Sinulla ei ole oikeutta asettaa tyyppiä tälle pyyntö." @@ -2610,15 +2610,15 @@ msgstr "Tuleva kierros" msgid "Project End" msgstr "Projektin loppu" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -3027,15 +3027,15 @@ msgstr "Tuoteomistaja" msgid "Stakeholder" msgstr "Sidosryhmä" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 0cb822b2..82f0e823 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-21 06:58+0000\n" "Last-Translator: Djyp Forest Fortin \n" "Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" @@ -353,12 +353,12 @@ msgstr "Erreur d'intégrité ou arguments invalides" msgid "Precondition error" msgstr "Erreur de précondition" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Erreur dans les types de paramètres de filtres" -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' doit être une valeur entière." @@ -880,8 +880,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Le payload n'est pas un json valide" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "Le projet n'existe pas" @@ -1186,19 +1186,19 @@ msgstr "Administrer les paramètres du projet" msgid "Admin roles" msgstr "Administrer les rôles" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Nom de modèle non valide" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Description du modèle non valide" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Au moins un utilisateur doit être un administrateur actif" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" @@ -1468,23 +1468,23 @@ msgstr "note bloquée" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "Vous n'avez pas la permission d'affecter ce sprint à ce problème." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "Vous n'avez pas la permission d'affecter ce statut à ce problème." -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "Vous n'avez pas la permission d'affecter cette sévérité à ce problème." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "Vous n'avez pas la permission d'affecter cette priorité à ce problème." -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "Vous n'avez pas la permission d'affecter ce type à ce problème." @@ -2354,15 +2354,15 @@ msgstr "Sprint futurs" msgid "Project End" msgstr "Fin du projet" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2759,15 +2759,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Participant" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index b37c7ec8..2f7b9cd0 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-06 14:06+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" @@ -341,12 +341,12 @@ msgstr "Integriteitsfout voor verkeerde of ongeldige argumenten" msgid "Precondition error" msgstr "Preconditie fout" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Fout in filter params types." -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' moet een integer waarde zijn." @@ -843,8 +843,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "De payload is geen geldige json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "Het project bestaat niet" @@ -1149,19 +1149,19 @@ msgstr "Admin project waarden" msgid "Admin roles" msgstr "Admin rollen" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Ongeldige template naam" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Ongeldige template omschrijving" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Minstens één van de gebruikers moet een active admin zijn" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." @@ -1431,25 +1431,25 @@ msgstr "geblokkeerde notitie" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "Je hebt geen toestemming om deze sprint op deze issue te zetten." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "Je hebt geen toestemming om deze status toe te kennen aan dze issue." -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Je hebt geen toestemming om dit ernstniveau toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Je hebt geen toestemming om deze prioriteit toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "Je hebt geen toestemming om dit type toe te kennen aan deze issue." @@ -2330,15 +2330,15 @@ msgstr "Toekomstige sprint" msgid "Project End" msgstr "Project einde" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2722,15 +2722,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 54f55ac7..1fedd7a1 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-07-26 13:15+0000\n" "Last-Translator: Konrad Krawczuk \n" "Language-Team: Polish (http://www.transifex.com/projects/p/taiga-back/" @@ -334,12 +334,12 @@ msgstr "Błąd integralności dla błędnych lub nieprawidłowych argumentów" msgid "Precondition error" msgstr "Błąd warunków wstępnych" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "Błąd w parametrach typów filtrów." -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "'project' musi być wartością typu int." @@ -909,8 +909,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Źródło nie jest prawidłowym plikiem json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "Projekt nie istnieje" @@ -1255,19 +1255,19 @@ msgstr "Administruj wartościami projektu" msgid "Admin roles" msgstr "Administruj rolami" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "Nieprawidłowa nazwa szablonu" -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "Nieprawidłowy opis szablonu" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "Przynajmniej jeden użytkownik musi być aktywnym Administratorem" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "Nie masz uprawnień by to zobaczyć." @@ -1537,23 +1537,23 @@ msgstr "zaglokowana notatka" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "Nie masz uprawnień do połączenia tego zgłoszenia ze sprintem." -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "Nie masz uprawnień do ustawienia rygoru dla tego zgłoszenia." -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "Nie masz uprawnień do ustawienia priorytetu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "Nie masz uprawnień do ustawienia typu dla tego zgłoszenia." @@ -2665,16 +2665,16 @@ msgstr "Przyszły sprint" msgid "Project End" msgstr "Zakończenie projektu" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "Nie masz uprawnień do ustawiania sprintu dla tego zadania." -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "" "Nie masz uprawnień do ustawiania historyjki użytkownika dla tego zadania" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "Nie masz uprawnień do ustawiania statusu dla tego zadania" @@ -3085,17 +3085,17 @@ msgstr "Właściciel produktu" msgid "Stakeholder" msgstr "Interesariusz" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Nie masz uprawnień do ustawiania sprintu dla tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "" "Nie masz uprawnień do ustawiania statusu do tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 00000000..99cc19e8 --- /dev/null +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,3476 @@ +# taiga-back.taiga. +# Copyright (C) 2015 Taiga Dev Team +# This file is distributed under the same license as the taiga-back package. +# +# Translators: +# Dmitry Vinokurov , 2015 +# Марат , 2015 +msgid "" +msgstr "" +"Project-Id-Version: taiga-back\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" +"PO-Revision-Date: 2015-08-01 02:52+0000\n" +"Last-Translator: Марат \n" +"Language-Team: Russian (http://www.transifex.com/projects/p/taiga-back/" +"language/ru/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: ru\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n" +"%100>=11 && n%100<=14)? 2 : 3);\n" + +#: taiga/auth/api.py:99 +msgid "Public register is disabled." +msgstr "Публичная регистрация отключена." + +#: taiga/auth/api.py:132 +msgid "invalid register type" +msgstr "неправильный тип регистра" + +#: taiga/auth/api.py:145 +msgid "invalid login type" +msgstr "неправильный тип логина" + +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +msgid "invalid username" +msgstr "неправильное имя пользователя" + +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +msgid "" +"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" +msgstr "Обязательно. 255 символов или меньше. Буквы, числа и символы /./-/_ '" + +#: taiga/auth/services.py:75 +msgid "Username is already in use." +msgstr "Это имя пользователя уже используется" + +#: taiga/auth/services.py:78 +msgid "Email is already in use." +msgstr "Этот адрес почты уже используется." + +#: taiga/auth/services.py:94 +msgid "Token not matches any valid invitation." +msgstr "Идентификатор не подходит ни под одно корректное приглашение." + +#: taiga/auth/services.py:122 +msgid "User is already registered." +msgstr "Пользователь уже зарегистрирован." + +#: taiga/auth/services.py:146 +msgid "Membership with user is already exists." +msgstr "Членство с этим пользователем уже существует." + +#: taiga/auth/services.py:172 +msgid "Error on creating new user." +msgstr "Ошибка при создании нового пользователя." + +#: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +msgid "Invalid token" +msgstr "Неверный идентификатор" + +#: taiga/base/api/fields.py:268 +msgid "This field is required." +msgstr "Это поле обязательно." + +#: taiga/base/api/fields.py:269 taiga/base/api/relations.py:311 +msgid "Invalid value." +msgstr "Неправильное значение." + +#: taiga/base/api/fields.py:453 +#, python-format +msgid "'%s' value must be either True or False." +msgstr "значение '%s' должно быть True - верно - или False - ложно." + +#: taiga/base/api/fields.py:517 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Введите корректное 'ссылочное имя' состоящее из букв, чисел, подчёркиваний и " +"дефисов." + +#: taiga/base/api/fields.py:532 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "" +"Выберите правильное значение. %(value)s не является одним из доступных " +"значений." + +#: taiga/base/api/fields.py:595 +msgid "Enter a valid email address." +msgstr "Введите правильный адрес email." + +#: taiga/base/api/fields.py:637 +#, python-format +msgid "Date has wrong format. Use one of these formats instead: %s" +msgstr "Дата имеет неверный формат. Воспользуйтесь одним из этих форматов: %s" + +#: taiga/base/api/fields.py:701 +#, python-format +msgid "Datetime has wrong format. Use one of these formats instead: %s" +msgstr "" +"Дата и время имеют неправильный формат. Воспользуйтесь одним из этих " +"форматов: %s" + +#: taiga/base/api/fields.py:771 +#, python-format +msgid "Time has wrong format. Use one of these formats instead: %s" +msgstr "" +"Время имеет неправильный формат. Воспользуйтесь одним из этих форматов: %s" + +#: taiga/base/api/fields.py:828 +msgid "Enter a whole number." +msgstr "Введите целое число." + +#: taiga/base/api/fields.py:829 taiga/base/api/fields.py:882 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Убедитесь, что это значение меньше или равно %(limit_value)s." + +#: taiga/base/api/fields.py:830 taiga/base/api/fields.py:883 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Убедитесь, что это значение больше или равно %(limit_value)s." + +#: taiga/base/api/fields.py:860 +#, python-format +msgid "\"%s\" value must be a float." +msgstr "\"%s\" значение должно быть числом с плавающей точкой." + +#: taiga/base/api/fields.py:881 +msgid "Enter a number." +msgstr "Введите число." + +#: taiga/base/api/fields.py:884 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "Убедитесь, что здесь всего не больше %s цифр." + +#: taiga/base/api/fields.py:885 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "Убедитесь, что здесь не больше %s цифр после точкой." + +#: taiga/base/api/fields.py:886 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "Убедитесь, что здесь не больше %s цифр перед точкой." + +#: taiga/base/api/fields.py:953 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Файл не был отправлен. Проверьте тип кодировки на форме." + +#: taiga/base/api/fields.py:954 +msgid "No file was submitted." +msgstr "Файл не был отправлен." + +#: taiga/base/api/fields.py:955 +msgid "The submitted file is empty." +msgstr "Отправленный файл пуст." + +#: taiga/base/api/fields.py:956 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "" +"Убедитесь, что имя этого файла имеет не больше %(max)d букв (сейчас - " +"%(length)d)." + +#: taiga/base/api/fields.py:957 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Пожалуйста, или отправьте файл, или снимите флажок." + +#: taiga/base/api/fields.py:997 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Загрузите корректное изображение. Файл, который вы загрузили - либо не " +"изображение, либо не корректное изображение." + +#: taiga/base/api/pagination.py:115 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "Страница не является 'последней' и не может быть приведена к int." + +#: taiga/base/api/pagination.py:119 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Неправильная страница (%(page_number)s): %(message)s" + +#: taiga/base/api/permissions.py:61 +msgid "Invalid permission definition." +msgstr "Неправильное определение разрешения" + +#: taiga/base/api/relations.py:221 +#, python-format +msgid "Invalid pk '%s' - object does not exist." +msgstr "Неправильное значение ключа '%s' - объект не существует." + +#: taiga/base/api/relations.py:222 +#, python-format +msgid "Incorrect type. Expected pk value, received %s." +msgstr "Неверный тип. Ожидалось значение ключа, пришло %s." + +#: taiga/base/api/relations.py:310 +#, python-format +msgid "Object with %s=%s does not exist." +msgstr "Объект с %s=%s не существует." + +#: taiga/base/api/relations.py:346 +msgid "Invalid hyperlink - No URL match" +msgstr "Неправильная гиперссылка - нет подходящего URL" + +#: taiga/base/api/relations.py:347 +msgid "Invalid hyperlink - Incorrect URL match" +msgstr "Неправильная гиперссылка - URL не подходит" + +#: taiga/base/api/relations.py:348 +msgid "Invalid hyperlink due to configuration error" +msgstr "Неправильная гиперссылка из-за ошибки конфигурации" + +#: taiga/base/api/relations.py:349 +msgid "Invalid hyperlink - object does not exist." +msgstr "Неправильная ссылка - объект не существует." + +#: taiga/base/api/relations.py:350 +#, python-format +msgid "Incorrect type. Expected url string, received %s." +msgstr "Неверный тип. Ожидалась строка URL, получено %s." + +#: taiga/base/api/serializers.py:296 +msgid "Invalid data" +msgstr "Неправильные данные." + +#: taiga/base/api/serializers.py:388 +msgid "No input provided" +msgstr "Ввод отсутствует" + +#: taiga/base/api/serializers.py:548 +msgid "Cannot create a new item, only existing items may be updated." +msgstr "" +"Нельзя создать новые объект, только существующие объекты могут быть изменены." + +#: taiga/base/api/serializers.py:559 +msgid "Expected a list of items." +msgstr "Ожидался список объектов." + +#: taiga/base/api/views.py:100 +msgid "Not found" +msgstr "Не найдено" + +#: taiga/base/api/views.py:103 +msgid "Permission denied" +msgstr "Доступ запрещён" + +#: taiga/base/api/views.py:451 +msgid "Server application error" +msgstr "Ошибка приложения на сервере" + +#: taiga/base/connectors/exceptions.py:24 +msgid "Connection error." +msgstr "Ошибка соединения." + +#: taiga/base/exceptions.py:53 +msgid "Malformed request." +msgstr "Неверное сформированный запрос." + +#: taiga/base/exceptions.py:58 +msgid "Incorrect authentication credentials." +msgstr "Неверные данные для аутентификации." + +#: taiga/base/exceptions.py:63 +msgid "Authentication credentials were not provided." +msgstr "Данные для аутентификации не предоставлены." + +#: taiga/base/exceptions.py:68 +msgid "You do not have permission to perform this action." +msgstr "У вас нет разрешения для этого действия." + +#: taiga/base/exceptions.py:73 +#, python-format +msgid "Method '%s' not allowed." +msgstr "Метод '%s' не разрешён." + +#: taiga/base/exceptions.py:81 +msgid "Could not satisfy the request's Accept header" +msgstr "Не удалось соответствовать заголовку принятия для этого запроса" + +#: taiga/base/exceptions.py:90 +#, python-format +msgid "Unsupported media type '%s' in request." +msgstr "Не поддерживаемый тип медиа '%s' в запросе." + +#: taiga/base/exceptions.py:98 +msgid "Request was throttled." +msgstr "Запрос был замят" + +#: taiga/base/exceptions.py:99 +#, python-format +msgid "Expected available in %d second%s." +msgstr "Будет доступно в течение %d секунд%s." + +#: taiga/base/exceptions.py:113 +msgid "Unexpected error" +msgstr "Неожиданная ошибка" + +#: taiga/base/exceptions.py:125 +msgid "Not found." +msgstr "Не найдено." + +#: taiga/base/exceptions.py:130 +msgid "Method not supported for this endpoint." +msgstr "Метод не поддерживается с этого конца." + +#: taiga/base/exceptions.py:138 taiga/base/exceptions.py:146 +msgid "Wrong arguments." +msgstr "Неправильные аргументы." + +#: taiga/base/exceptions.py:150 +msgid "Data validation error" +msgstr "Ошибка при проверке данных" + +#: taiga/base/exceptions.py:162 +msgid "Integrity Error for wrong or invalid arguments" +msgstr "Ошибка целостности из-за неправильных параметров" + +#: taiga/base/exceptions.py:169 +msgid "Precondition error" +msgstr "Ошибка предусловия" + +#: taiga/base/filters.py:79 +msgid "Error in filter params types." +msgstr "Ошибка в типах фильтров для параметров." + +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 +msgid "'project' must be an integer value." +msgstr "'project' должно быть целым значением." + +#: taiga/base/tags.py:25 +msgid "tags" +msgstr "тэги" + +#: taiga/base/templates/emails/base-body-html.jinja:6 +msgid "Taiga" +msgstr "Taiga" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Follow us on Twitter" +msgstr "Следите за нами в Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Twitter" +msgstr "Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "Get the code on GitHub" +msgstr "Скачайте код на GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "GitHub" +msgstr "GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Visit our website" +msgstr "Посетите наш вебсайт" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Taiga.io" +msgstr "Taiga.io" + +#: taiga/base/templates/emails/base-body-html.jinja:423 +#: taiga/base/templates/emails/hero-body-html.jinja:397 +#: taiga/base/templates/emails/updates-body-html.jinja:459 +#, python-format +msgid "" +"\n" +" Taiga Support:\n" +" %(support_url)s\n" +"
\n" +" Contact us:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Mailing list:\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " +msgstr "" +"\n" +" ПоддержкаTaiga:\n" +" %(support_url)s\n" +"
\n" +" Свяжитесь с нами:" +"\n" +"
\n" +" %(support_email)s\n" +" \n" +"
\n" +" Рассылка:\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " + +#: taiga/base/templates/emails/hero-body-html.jinja:6 +msgid "You have been Taigatized" +msgstr "Тайга поимела вас" + +#: taiga/base/templates/emails/hero-body-html.jinja:359 +msgid "" +"\n" +"

You have been Taigatized!" +"

\n" +"

Welcome to Taiga, an Open " +"Source, Agile Project Management Tool

\n" +" " +msgstr "" +"\n" +"

Тайга поимела вас!

\n" +"

Добро пожаловать в Taiga " +"- инструмент с открытым исходным кодом для управления проектами в стиле " +"Agile

\n" +" " + +#: taiga/base/templates/emails/updates-body-html.jinja:6 +msgid "[Taiga] Updates" +msgstr "[Taiga] Обновления" + +#: taiga/base/templates/emails/updates-body-html.jinja:417 +msgid "Updates" +msgstr "Обновления" + +#: taiga/base/templates/emails/updates-body-html.jinja:423 +#, python-format +msgid "" +"\n" +"

comment:" +"

\n" +"

" +"%(comment)s

\n" +" " +msgstr "" +"\n" +"

комментарий:" +"

\n" +"

" +"%(comment)s

\n" +" " + +#: taiga/base/templates/emails/updates-body-text.jinja:6 +#, python-format +msgid "" +"\n" +" Comment: %(comment)s\n" +" " +msgstr "" +"\n" +" Комментарий: %(comment)s\n" +" " + +#: taiga/export_import/api.py:103 +msgid "We needed at least one role" +msgstr "Нам была нужна хотя бы одна роль" + +#: taiga/export_import/api.py:197 +msgid "Needed dump file" +msgstr "Нужен свалочный файл" + +#: taiga/export_import/api.py:204 +msgid "Invalid dump format" +msgstr "Неправильный формат для свалки" + +#: taiga/export_import/dump_service.py:96 +msgid "error importing project data" +msgstr "ошибка импорта данных по проекту" + +#: taiga/export_import/dump_service.py:109 +msgid "error importing lists of project attributes" +msgstr "ошибка импорта списка атрибутов проекта" + +#: taiga/export_import/dump_service.py:114 +msgid "error importing default project attributes values" +msgstr "ошибка импорта значение по умолчанию для атрибутов проекта" + +#: taiga/export_import/dump_service.py:124 +msgid "error importing custom attributes" +msgstr "ошибка импорта специальных атрибутов" + +#: taiga/export_import/dump_service.py:129 +msgid "error importing roles" +msgstr "ошибка импорта ролей" + +#: taiga/export_import/dump_service.py:144 +msgid "error importing memberships" +msgstr "ошибка импорта членства" + +#: taiga/export_import/dump_service.py:149 +msgid "error importing sprints" +msgstr "ошибка импорта спринтов" + +#: taiga/export_import/dump_service.py:154 +msgid "error importing wiki pages" +msgstr "ошибка импорта вики-страниц" + +#: taiga/export_import/dump_service.py:159 +msgid "error importing wiki links" +msgstr "ошибка импорта вики-ссылок" + +#: taiga/export_import/dump_service.py:164 +msgid "error importing issues" +msgstr "ошибка импорта проблем" + +#: taiga/export_import/dump_service.py:169 +msgid "error importing user stories" +msgstr "ошибка импорта историй от пользователей" + +#: taiga/export_import/dump_service.py:174 +msgid "error importing tasks" +msgstr "ошибка импорта задач" + +#: taiga/export_import/dump_service.py:179 +msgid "error importing tags" +msgstr "ошибка импорта тэгов" + +#: taiga/export_import/dump_service.py:183 +msgid "error importing timelines" +msgstr "ошибка импорта графиков" + +#: taiga/export_import/serializers.py:161 +msgid "{}=\"{}\" not found in this project" +msgstr "{}=\"{}\" не найдено в этом проекте" + +#: taiga/export_import/serializers.py:384 +#: taiga/projects/custom_attributes/serializers.py:103 +msgid "Invalid content. It must be {\"key\": \"value\",...}" +msgstr "Неправильные данные. Должны быть в формате {\"key\": \"value\",...}" + +#: taiga/export_import/serializers.py:399 +#: taiga/projects/custom_attributes/serializers.py:118 +msgid "It contain invalid custom fields." +msgstr "Содержит неверные специальные поля" + +#: taiga/export_import/serializers.py:468 +#: taiga/projects/milestones/serializers.py:63 +#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 +#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +msgid "Name duplicated for the project" +msgstr "Уже есть такое имя для проекта" + +#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +msgid "Error generating project dump" +msgstr "Ошибка создания свалочного файла для проекта" + +#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +msgid "Error loading project dump" +msgstr "Ошибка загрузки свалочного файла проекта" + +#: taiga/export_import/templates/emails/dump_project-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump generated

\n" +"

Hello %(user)s,

\n" +"

Your dump from project %(project)s has been correctly generated.\n" +"

You can download it here:

\n" +" Download the dump file\n" +"

This file will be deleted on %(deletion_date)s.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Создан свалочный файл проекта

\n" +"

Здравствуйте, %(user)s,

\n" +"

Ваш свалочный файл для проекта %(project)s успешно сгенерирован.\n" +"

Вы можете скачать его здесь:

\n" +" Скачать свалочный файл\n" +"

Этот файл будет удалён %(deletion_date)s.

\n" +"

Команда Taiga

\n" +" " + +#: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your dump from project %(project)s has been correctly generated. You can " +"download it here:\n" +"\n" +"%(url)s\n" +"\n" +"This file will be deleted on %(deletion_date)s.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Здравствуйте, %(user)s,\n" +"\n" +"Ваш свалочный файл для проекта %(project)s успешно сгенерирован. Вы можете " +"скачать его здесь:\n" +"\n" +"%(url)s\n" +"\n" +"Файл будет удалён %(deletion_date)s.\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/export_import/templates/emails/dump_project-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been generated" +msgstr "[%(project)s] Свалочный файл вашего проекта успешно сгенерирован" + +#: taiga/export_import/templates/emails/export_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project %(project)s has not been exported correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Здравствуйте, %(user)s,

\n" +"

Ваш проект %(project)s не был корректно экспортирован.

\n" +"

Системные администраторы Taiga были проинформированы об этом.
" +"Пожалуйста, попробуйте ещё раз или свяжитесь со службой поддержки\n" +" %(support_email)s

\n" +"

Команда Taiga

\n" +" " + +#: taiga/export_import/templates/emails/export_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"Your project %(project)s has not been exported correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Здравствуйте, %(user)s,\n" +"\n" +"%(error_message)s\n" +"Ваш проект %(project)s не был корректно экспортирован.\n" +"\n" +"Системные администраторы Taiga уведомлены об этом.\n" +"\n" +"Пожалуйста, попробуйте ещё раз или свяжитесь со службой поддержки " +"%(support_email)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/export_import/templates/emails/export_error-subject.jinja:1 +#, python-format +msgid "[%(project)s] %(error_subject)s" +msgstr "[%(project)s] %(error_subject)s" + +#: taiga/export_import/templates/emails/import_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project has not been importer correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Здравствуйте, %(user)s,

\n" +"

Ваш проект не был корректно импортирован.

\n" +"

Системные администраторы Taiga были проинформированы об этом.
" +"Пожалуйста, попробуйте ещё раз или свяжитесь со службой поддержки\n" +" %(support_email)s

\n" +"

Команда Taiga

" + +#: taiga/export_import/templates/emails/import_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Your project has not been importer correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Здравствуйте, %(user)s,\n" +"\n" +"%(error_message)s\n" +"Ваш проект не был корректно импортирован.\n" +"\n" +"Системные администраторы Taiga уведомлены об этом.\n" +"\n" +"Пожалуйста, попробуйте ещё раз или свяжитесь со службой поддержки " +"%(support_email)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/export_import/templates/emails/import_error-subject.jinja:1 +#, python-format +msgid "[Taiga] %(error_subject)s" +msgstr "[Taiga] %(error_subject)s" + +#: taiga/export_import/templates/emails/load_dump-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump imported

\n" +"

Hello %(user)s,

\n" +"

Your project dump has been correctly imported.

\n" +" Go to %(project)s\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Свалочный файл проекта импортирован

\n" +"

Здравствуйте, %(user)s,

\n" +"

Свалочный файл вашего проекта успешно импортирован.

\n" +" Перейти к %(project)s\n" +"

Команда Taiga

\n" +" " + +#: taiga/export_import/templates/emails/load_dump-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your project dump has been correctly imported.\n" +"\n" +"You can see the project %(project)s here:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Здравствуйте, %(user)s,\n" +"\n" +"Свалочный файл вашего проекта успешно импортирован.\n" +"\n" +"Вы можете посмотреть %(project)s здесь:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/export_import/templates/emails/load_dump-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been imported" +msgstr "[%(project)s] Свалочный файл вашего проекта импортирован" + +#: taiga/feedback/models.py:23 taiga/users/models.py:111 +msgid "full name" +msgstr "полное имя" + +#: taiga/feedback/models.py:25 taiga/users/models.py:106 +msgid "email address" +msgstr "адрес email" + +#: taiga/feedback/models.py:27 +msgid "comment" +msgstr "комментарий" + +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 +#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 +#: taiga/projects/models.py:129 taiga/projects/models.py:561 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +msgid "created date" +msgstr "дата создания" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Feedback

\n" +"

Taiga has received feedback from %(full_name)s <%(email)s>

\n" +" " +msgstr "" +"\n" +"

Отзыв

\n" +"

Taiga получила отзывы от %(full_name)s <%(email)s>

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:9 +#, python-format +msgid "" +"\n" +"

Comment

\n" +"

%(comment)s

\n" +" " +msgstr "" +"\n" +"

Комментарий

\n" +"

%(comment)s

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 +#: taiga/users/admin.py:51 +msgid "Extra info" +msgstr "Дополнительное инфо" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:1 +#, python-format +msgid "" +"---------\n" +"- From: %(full_name)s <%(email)s>\n" +"---------\n" +"- Comment:\n" +"%(comment)s\n" +"---------" +msgstr "" +"---------\n" +"- От: %(full_name)s <%(email)s>\n" +"---------\n" +"- Комментарий:\n" +"%(comment)s\n" +"---------" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:8 +msgid "- Extra info:" +msgstr "- Дополнительное инфо:" + +#: taiga/feedback/templates/emails/feedback_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Feedback from %(full_name)s <%(email)s>\n" +msgstr "" +"\n" +"[Taiga] Отзыв от %(full_name)s <%(email)s>\n" + +#: taiga/hooks/api.py:52 +msgid "The payload is not a valid json" +msgstr "Нагрузочный файл не является правильным json-файлом" + +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +msgid "The project doesn't exist" +msgstr "Проект не существует" + +#: taiga/hooks/api.py:64 +msgid "Bad signature" +msgstr "Плохая подпись" + +#: taiga/hooks/bitbucket/event_hooks.py:73 +#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +msgid "The referenced element doesn't exist" +msgstr "Указанный элемент не существует" + +#: taiga/hooks/bitbucket/event_hooks.py:80 +#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +msgid "The status doesn't exist" +msgstr "Статус не существует" + +#: taiga/hooks/bitbucket/event_hooks.py:86 +msgid "Status changed from BitBucket commit" +msgstr "Статус изменён из-за вклада с BitBucket" + +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Неверная информация о проблеме" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Проблема создана [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " +"профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" +"Изначальная проблема на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Перейти к 'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "Проблема создана из BitBucket." + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Неправильная информация о комментарии к проблеме" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Комментарий от [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " +"профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" +"Изначальная проблема на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Перейти к 'bb#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" +"Комментарий от BitBucket:\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:96 +#, python-brace-format +msgid "" +"Status changed by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." +msgstr "" +"Статус изменён пользователем [@{github_user_name}]({github_user_url} " +"\"Посмотреть профиль @{github_user_name} на GitHub\") из-за вклада на GitHub " +"[{commit_id}]({commit_url} \"Посмотреть вклад '{commit_id} - " +"{commit_message}'\")." + +#: taiga/hooks/github/event_hooks.py:107 +msgid "Status changed from GitHub commit." +msgstr "Статус изменён из-за вклада на GitHub." + +#: taiga/hooks/github/event_hooks.py:157 +#, python-brace-format +msgid "" +"Issue created by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Проблема создана [@{github_user_name}]({github_user_url} \"Посмотреть " +"профиль @{github_user_name} на GitHub\") из GitHub.\n" +"Изначальная проблема на GitHub: [gh#{number} - {subject}]({github_url} " +"\"Перейти к 'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/github/event_hooks.py:168 +msgid "Issue created from GitHub." +msgstr "Проблема создана из GitHub." + +#: taiga/hooks/github/event_hooks.py:200 +#, python-brace-format +msgid "" +"Comment by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Комментарий от [@{github_user_name}]({github_user_url} \"Посмотреть профиль " +"@{github_user_name} на GitHub\") из GitHub.\n" +"Изначальная проблема на GitHub: [gh#{number} - {subject}]({github_url} " +"\"Перейти к 'gh#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:211 +#, python-brace-format +msgid "" +"Comment From GitHub:\n" +"\n" +"{message}" +msgstr "" +"Комментарий из GitHub:\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:86 +msgid "Status changed from GitLab commit" +msgstr "Статус изменён из-за вклада на GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:128 +msgid "Created from GitLab" +msgstr "Создано из GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Комментарий от [@{gitlab_user_name}]({gitlab_user_url} \"Посмотреть профиль " +"@{gitlab_user_name} на GitLab\") из GitLab.\n" +"Изначальная проблема на GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go " +"to 'gl#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" +"Комментарий из GitLab:\n" +"\n" +"{message}" + +#: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 +#: taiga/permissions/permissions.py:52 +msgid "View project" +msgstr "Просмотреть проект" + +#: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 +#: taiga/permissions/permissions.py:54 +msgid "View milestones" +msgstr "Просмотреть вехи" + +#: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 +msgid "View user stories" +msgstr "Просмотреть пользовательские истории" + +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:64 +msgid "View tasks" +msgstr "Просмотреть задачи" + +#: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 +#: taiga/permissions/permissions.py:69 +msgid "View issues" +msgstr "Посмотреть проблемы" + +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:75 +msgid "View wiki pages" +msgstr "Просмотреть wiki-страницы" + +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 +#: taiga/permissions/permissions.py:80 +msgid "View wiki links" +msgstr "Просмотреть wiki-ссылки" + +#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 +msgid "Vote issues" +msgstr "Проголосовать по проблемам" + +#: taiga/permissions/permissions.py:39 +msgid "Request membership" +msgstr "Запросить членство" + +#: taiga/permissions/permissions.py:40 +msgid "Add user story to project" +msgstr "Добавить пользовательскую историю к проекту" + +#: taiga/permissions/permissions.py:41 +msgid "Add comments to user stories" +msgstr "Добавить комментарии к пользовательским историям" + +#: taiga/permissions/permissions.py:42 +msgid "Add comments to tasks" +msgstr "Добавить комментарии к задачам" + +#: taiga/permissions/permissions.py:43 +msgid "Add issues" +msgstr "Добавить проблемы" + +#: taiga/permissions/permissions.py:44 +msgid "Add comments to issues" +msgstr "Добавить комментарии к проблемам" + +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +msgid "Add wiki page" +msgstr "Создать wiki-страницу" + +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +msgid "Modify wiki page" +msgstr "Изменить wiki-страницу" + +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +msgid "Add wiki link" +msgstr "Добавить wiki-ссылку" + +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +msgid "Modify wiki link" +msgstr "Изменить wiki-ссылку" + +#: taiga/permissions/permissions.py:55 +msgid "Add milestone" +msgstr "Добавить веху" + +#: taiga/permissions/permissions.py:56 +msgid "Modify milestone" +msgstr "Изменить веху" + +#: taiga/permissions/permissions.py:57 +msgid "Delete milestone" +msgstr "Удалить веху" + +#: taiga/permissions/permissions.py:59 +msgid "View user story" +msgstr "Просмотреть пользовательскую историю" + +#: taiga/permissions/permissions.py:60 +msgid "Add user story" +msgstr "Добавить пользовательскую историю" + +#: taiga/permissions/permissions.py:61 +msgid "Modify user story" +msgstr "Изменить пользовательскую историю" + +#: taiga/permissions/permissions.py:62 +msgid "Delete user story" +msgstr "Удалить пользовательскую историю" + +#: taiga/permissions/permissions.py:65 +msgid "Add task" +msgstr "Добавить задачу" + +#: taiga/permissions/permissions.py:66 +msgid "Modify task" +msgstr "Изменить задачу" + +#: taiga/permissions/permissions.py:67 +msgid "Delete task" +msgstr "Удалить задачу" + +#: taiga/permissions/permissions.py:71 +msgid "Add issue" +msgstr "Добавить проблему" + +#: taiga/permissions/permissions.py:72 +msgid "Modify issue" +msgstr "Изменить проблему" + +#: taiga/permissions/permissions.py:73 +msgid "Delete issue" +msgstr "Удалить проблему" + +#: taiga/permissions/permissions.py:78 +msgid "Delete wiki page" +msgstr "Удалить wiki-страницу" + +#: taiga/permissions/permissions.py:83 +msgid "Delete wiki link" +msgstr "Удалить wiki-ссылку" + +#: taiga/permissions/permissions.py:87 +msgid "Modify project" +msgstr "Изменить проект" + +#: taiga/permissions/permissions.py:88 +msgid "Add member" +msgstr "Добавить участника" + +#: taiga/permissions/permissions.py:89 +msgid "Remove member" +msgstr "Удалить участника" + +#: taiga/permissions/permissions.py:90 +msgid "Delete project" +msgstr "Удалить проект" + +#: taiga/permissions/permissions.py:91 +msgid "Admin project values" +msgstr "Управлять значениями проекта" + +#: taiga/permissions/permissions.py:92 +msgid "Admin roles" +msgstr "Управлять ролями" + +#: taiga/projects/api.py:198 +msgid "Not valid template name" +msgstr "Неверное название шаблона" + +#: taiga/projects/api.py:201 +msgid "Not valid template description" +msgstr "Неверное описание шаблона" + +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +msgid "At least one of the user must be an active admin" +msgstr "" +"По крайней мере один пользователь должен быть активным администратором." + +#: taiga/projects/api.py:499 +msgid "You don't have permisions to see that." +msgstr "У вас нет разрешения на просмотр." + +#: taiga/projects/attachments/api.py:47 +msgid "Non partial updates not supported" +msgstr "Частичные обновления не поддерживаются" + +#: taiga/projects/attachments/api.py:62 +msgid "Project ID not matches between object and project" +msgstr "Идентификатор проекта не подходит к этому объекту" + +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 +#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/userstorage/models.py:25 +msgid "owner" +msgstr "владелец" + +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 +#: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 +#: taiga/projects/models.py:338 taiga/projects/models.py:364 +#: taiga/projects/models.py:395 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 +#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +msgid "project" +msgstr "проект" + +#: taiga/projects/attachments/models.py:56 +msgid "content type" +msgstr "тип содержимого" + +#: taiga/projects/attachments/models.py:58 +msgid "object id" +msgstr "идентификатор объекта" + +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 +#: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 +#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 +#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +msgid "modified date" +msgstr "изменённая дата" + +#: taiga/projects/attachments/models.py:69 +msgid "attached file" +msgstr "приложенный файл" + +#: taiga/projects/attachments/models.py:72 +msgid "is deprecated" +msgstr "устаревшее" + +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 +#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "описание" + +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 +#: taiga/projects/models.py:391 taiga/projects/models.py:418 +#: taiga/projects/models.py:453 taiga/projects/models.py:476 +#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +msgid "order" +msgstr "порядок" + +#: taiga/projects/choices.py:21 +msgid "AppearIn" +msgstr "AppearIn" + +#: taiga/projects/choices.py:22 +msgid "Jitsi" +msgstr "Jitsi" + +#: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "Специальный" + +#: taiga/projects/choices.py:24 +msgid "Talky" +msgstr "Talky" + +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "Текст" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "Многострочный текст" + +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 +#: taiga/projects/models.py:350 taiga/projects/models.py:389 +#: taiga/projects/models.py:414 taiga/projects/models.py:451 +#: taiga/projects/models.py:474 taiga/projects/models.py:497 +#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/users/models.py:183 taiga/webhooks/models.py:27 +msgid "name" +msgstr "имя" + +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "тип" + +#: taiga/projects/custom_attributes/models.py:87 +msgid "values" +msgstr "значения" + +#: taiga/projects/custom_attributes/models.py:97 +#: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 +msgid "user story" +msgstr "пользовательская история" + +#: taiga/projects/custom_attributes/models.py:112 +msgid "task" +msgstr "задача" + +#: taiga/projects/custom_attributes/models.py:127 +msgid "issue" +msgstr "проблема" + +#: taiga/projects/custom_attributes/serializers.py:57 +msgid "Already exists one with the same name." +msgstr "Это имя уже используется." + +#: taiga/projects/history/api.py:70 +msgid "Comment already deleted" +msgstr "Комментарий уже был удалён" + +#: taiga/projects/history/api.py:89 +msgid "Comment not deleted" +msgstr "Комментарий не удалён" + +#: taiga/projects/history/choices.py:27 +msgid "Change" +msgstr "Изменить" + +#: taiga/projects/history/choices.py:28 +msgid "Create" +msgstr "Создать" + +#: taiga/projects/history/choices.py:29 +msgid "Delete" +msgstr "Удалить" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:22 +#, python-format +msgid "%(role)s role points" +msgstr "очки для роли %(role)s" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:25 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:130 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:133 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:156 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:193 +msgid "from" +msgstr "от" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:31 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:141 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:144 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:162 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:179 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:199 +msgid "to" +msgstr "кому" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:43 +msgid "Added new attachment" +msgstr "Добавлено новое вложение" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:61 +msgid "Updated attachment" +msgstr "Вложение обновлено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:67 +msgid "deprecated" +msgstr "устаревшее" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:69 +msgid "not deprecated" +msgstr "не устаревшее" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:85 +msgid "Deleted attachment" +msgstr "Удалённое вложение" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:104 +msgid "added" +msgstr "добавлено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:109 +msgid "removed" +msgstr "удалено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 +#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +msgid "Unassigned" +msgstr "Не назначено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:211 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:86 +msgid "-deleted-" +msgstr "-удалено-" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "to:" +msgstr "кому:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "from:" +msgstr "от:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:26 +msgid "Added" +msgstr "Добавлено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:33 +msgid "Changed" +msgstr "Изменено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:40 +msgid "Deleted" +msgstr "Удалено" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:54 +msgid "added:" +msgstr "добавлено:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:57 +msgid "removed:" +msgstr "удалено:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:62 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:79 +msgid "From:" +msgstr "От:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:63 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:80 +msgid "To:" +msgstr "Кому:" + +#: taiga/projects/history/templatetags/functions.py:26 +#: taiga/projects/wiki/models.py:32 +msgid "content" +msgstr "содержимое" + +#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/mixins/blocked.py:31 +msgid "blocked note" +msgstr "Заметка о блокировке" + +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "спринт" + +#: taiga/projects/issues/api.py:154 +msgid "You don't have permissions to set this sprint to this issue." +msgstr "" +"У вас нет прав для того чтобы установить такой спринт для этой проблемы" + +#: taiga/projects/issues/api.py:158 +msgid "You don't have permissions to set this status to this issue." +msgstr "" +"У вас нет прав для того чтобы установить такой статус для этой проблемы" + +#: taiga/projects/issues/api.py:162 +msgid "You don't have permissions to set this severity to this issue." +msgstr "" +"У вас нет прав для того чтобы установить такую важность для этой проблемы" + +#: taiga/projects/issues/api.py:166 +msgid "You don't have permissions to set this priority to this issue." +msgstr "" +"У вас нет прав для того чтобы установить такой приоритет для этой проблемы" + +#: taiga/projects/issues/api.py:170 +msgid "You don't have permissions to set this type to this issue." +msgstr "У вас нет прав для того чтобы установить такой тип для этой проблемы" + +#: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 +#: taiga/projects/userstories/models.py:57 +msgid "ref" +msgstr "Ссылка" + +#: taiga/projects/issues/models.py:40 taiga/projects/tasks/models.py:39 +#: taiga/projects/userstories/models.py:67 +msgid "status" +msgstr "cтатус" + +#: taiga/projects/issues/models.py:42 +msgid "severity" +msgstr "важность" + +#: taiga/projects/issues/models.py:44 +msgid "priority" +msgstr "приоритет" + +#: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 +#: taiga/projects/userstories/models.py:60 +msgid "milestone" +msgstr "веха" + +#: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 +msgid "finished date" +msgstr "дата завершения" + +#: taiga/projects/issues/models.py:60 taiga/projects/tasks/models.py:53 +#: taiga/projects/userstories/models.py:89 +msgid "subject" +msgstr "тема" + +#: taiga/projects/issues/models.py:64 taiga/projects/tasks/models.py:63 +#: taiga/projects/userstories/models.py:93 +msgid "assigned to" +msgstr "назначено" + +#: taiga/projects/issues/models.py:66 taiga/projects/tasks/models.py:67 +#: taiga/projects/userstories/models.py:103 +msgid "external reference" +msgstr "внешняя ссылка" + +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:416 +#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +msgid "slug" +msgstr "ссылочное имя" + +#: taiga/projects/milestones/models.py:42 +msgid "estimated start date" +msgstr "предполагаемая дата начала" + +#: taiga/projects/milestones/models.py:43 +msgid "estimated finish date" +msgstr "предполагаемая дата завершения" + +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 +#: taiga/projects/models.py:420 taiga/projects/models.py:503 +msgid "is closed" +msgstr "закрыто" + +#: taiga/projects/milestones/models.py:52 +msgid "disponibility" +msgstr "доступность" + +#: taiga/projects/milestones/models.py:75 +msgid "The estimated start must be previous to the estimated finish." +msgstr "" +"Предполагаемая дата начала должна предшествовать предполагаемой дате " +"завершения." + +#: taiga/projects/milestones/validators.py:12 +msgid "There's no sprint with that id" +msgstr "Не существует спринта с таким идентификатором" + +#: taiga/projects/mixins/blocked.py:29 +msgid "is blocked" +msgstr "заблокировано" + +#: taiga/projects/mixins/ordering.py:47 +#, python-brace-format +msgid "'{param}' parameter is mandatory" +msgstr "параметр '{param}' является обязательным" + +#: taiga/projects/mixins/ordering.py:51 +msgid "'project' parameter is mandatory" +msgstr "параметр 'project' является обязательным" + +#: taiga/projects/models.py:59 +msgid "email" +msgstr "электронная почта" + +#: taiga/projects/models.py:61 +msgid "create at" +msgstr "создано" + +#: taiga/projects/models.py:63 taiga/users/models.py:128 +msgid "token" +msgstr "идентификатор" + +#: taiga/projects/models.py:69 +msgid "invitation extra text" +msgstr "дополнительный текст к приглашению" + +#: taiga/projects/models.py:72 +msgid "user order" +msgstr "порядок пользователей" + +#: taiga/projects/models.py:78 +msgid "The user is already member of the project" +msgstr "Этот пользователем уже является участником проекта" + +#: taiga/projects/models.py:93 +msgid "default points" +msgstr "очки по умолчанию" + +#: taiga/projects/models.py:97 +msgid "default US status" +msgstr "статусы ПИ по умолчанию" + +#: taiga/projects/models.py:101 +msgid "default task status" +msgstr "статус задачи по умолчанию" + +#: taiga/projects/models.py:104 +msgid "default priority" +msgstr "приоритет по умолчанию" + +#: taiga/projects/models.py:107 +msgid "default severity" +msgstr "важность по умолчанию" + +#: taiga/projects/models.py:111 +msgid "default issue status" +msgstr "статус проблемы по умолчанию" + +#: taiga/projects/models.py:115 +msgid "default issue type" +msgstr "тип проблемы по умолчанию" + +#: taiga/projects/models.py:136 +msgid "members" +msgstr "участники" + +#: taiga/projects/models.py:139 +msgid "total of milestones" +msgstr "общее количество вех" + +#: taiga/projects/models.py:140 +msgid "total story points" +msgstr "очки истории" + +#: taiga/projects/models.py:143 taiga/projects/models.py:570 +msgid "active backlog panel" +msgstr "активная панель списка задач" + +#: taiga/projects/models.py:145 taiga/projects/models.py:572 +msgid "active kanban panel" +msgstr "активная панель kanban" + +#: taiga/projects/models.py:147 taiga/projects/models.py:574 +msgid "active wiki panel" +msgstr "активная wiki-панель" + +#: taiga/projects/models.py:149 taiga/projects/models.py:576 +msgid "active issues panel" +msgstr "активная панель проблем" + +#: taiga/projects/models.py:152 taiga/projects/models.py:579 +msgid "videoconference system" +msgstr "система видеоконференций" + +#: taiga/projects/models.py:154 taiga/projects/models.py:581 +msgid "videoconference extra data" +msgstr "дополнительные данные системы видеоконференций" + +#: taiga/projects/models.py:159 +msgid "creation template" +msgstr "шаблон для создания" + +#: taiga/projects/models.py:162 +msgid "anonymous permissions" +msgstr "права анонимов" + +#: taiga/projects/models.py:166 +msgid "user permissions" +msgstr "права пользователя" + +#: taiga/projects/models.py:169 +msgid "is private" +msgstr "личное" + +#: taiga/projects/models.py:180 +msgid "tags colors" +msgstr "цвета тэгов" + +#: taiga/projects/models.py:339 +msgid "modules config" +msgstr "конфигурация модулей" + +#: taiga/projects/models.py:358 +msgid "is archived" +msgstr "архивировано" + +#: taiga/projects/models.py:360 taiga/projects/models.py:422 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/users/models.py:113 +msgid "color" +msgstr "цвет" + +#: taiga/projects/models.py:362 +msgid "work in progress limit" +msgstr "ограничение на активную работу" + +#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +msgid "value" +msgstr "значение" + +#: taiga/projects/models.py:567 +msgid "default owner's role" +msgstr "роль владельца по умолчанию" + +#: taiga/projects/models.py:583 +msgid "default options" +msgstr "параметры по умолчанию" + +#: taiga/projects/models.py:584 +msgid "us statuses" +msgstr "статусы ПИ" + +#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/userstories/models.py:72 +msgid "points" +msgstr "очки" + +#: taiga/projects/models.py:586 +msgid "task statuses" +msgstr "статусы задач" + +#: taiga/projects/models.py:587 +msgid "issue statuses" +msgstr "статусы проблем" + +#: taiga/projects/models.py:588 +msgid "issue types" +msgstr "типы проблем" + +#: taiga/projects/models.py:589 +msgid "priorities" +msgstr "приоритеты" + +#: taiga/projects/models.py:590 +msgid "severities" +msgstr "степени важности" + +#: taiga/projects/models.py:591 +msgid "roles" +msgstr "роли" + +#: taiga/projects/notifications/choices.py:28 +msgid "Not watching" +msgstr "Не отслеживаемое" + +#: taiga/projects/notifications/choices.py:29 +msgid "Watching" +msgstr "Отслеживаемое" + +#: taiga/projects/notifications/choices.py:30 +msgid "Ignoring" +msgstr "Игнорируется" + +#: taiga/projects/notifications/mixins.py:87 +msgid "watchers" +msgstr "наблюдатели" + +#: taiga/projects/notifications/models.py:59 +msgid "created date time" +msgstr "дата и время создания" + +#: taiga/projects/notifications/models.py:61 +msgid "updated date time" +msgstr "дата и время обновления" + +#: taiga/projects/notifications/models.py:63 +msgid "history entries" +msgstr "записи истории" + +#: taiga/projects/notifications/models.py:66 +msgid "notify users" +msgstr "уведомить пользователей" + +#: taiga/projects/notifications/services.py:63 +#: taiga/projects/notifications/services.py:77 +msgid "Notify exists for specified user and project" +msgstr "Уведомление существует для данных пользователя и проекта" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue updated

\n" +"

Hello %(user)s,
%(changer)s has updated an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +" " +msgstr "" +"\n" +"

Проблема обновлена

\n" +"

Привет %(user)s,
%(changer)s обновил(а) проблему в %(project)s\n" +"

Проблема #%(ref)s %(subject)s

\n" +" Просмотреть проблему\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Issue updated\n" +"Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"Проблема обновлена\n" +"Привет %(user)s, %(changer)s обновил(а) проблему %(project)s\n" +"Просмотреть проблему #%(ref)s %(subject)s можно по ссылке %(url)s\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Обновлена проблема #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New issue created

\n" +"

Hello %(user)s,
%(changer)s has created a new issue on " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Добавлена новая проблема

\n" +"

Привет %(user)s,
%(changer)s добавил(а) новую проблему в " +"%(project)s

\n" +"

Проблема #%(ref)s %(subject)s

\n" +" Просмотреть проблему\n" +"

Команда Тайги

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New issue created\n" +"Hello %(user)s, %(changer)s has created a new issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Добавлена новая проблема\n" +"Привет %(user)s, %(changer)s добавил(а) новую проблему в %(project)s\n" +"Просмотреть проблему #%(ref)s %(subject)s можно по ссылке %(url)s\n" +"\n" +"---\n" +"Команда Тайги\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Добавлена проблема #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Проблема удалена

\n" +"

Привет %(user)s,
%(changer)s удалил(а) проблему из %(project)s\n" +"

Проблема #%(ref)s %(subject)s

\n" +"

Команда Тайги

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Issue deleted\n" +"Hello %(user)s, %(changer)s has deleted an issue on %(project)s\n" +"Issue #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Проблема удалена\n" +"Привет %(user)s, %(changer)s удалил(а) проблему из %(project)s\n" +"Проблема #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Команда Тайги\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Удалена проблема #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint updated

\n" +"

Hello %(user)s,
%(changer)s has updated an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See sprint\n" +" " +msgstr "" +"\n" +"

Спринт обновлён

\n" +"

Привет %(user)s,
%(changer)s обновил(а) спринт в %(project)s\n" +"

Спринт %(name)s

\n" +" Просмотреть спринт\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Sprint updated\n" +"Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +msgstr "" +"\n" +"Спринт обновлён\n" +"Привет %(user)s, %(changer)s изменил(а) спринт в %(project)s\n" +"Просмотреть спринт %(name)s можно по ссылке %(url)s\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Обновлён спринт \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New sprint created

\n" +"

Hello %(user)s,
%(changer)s has created a new sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See " +"sprint\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Добавлен новый спринт

\n" +"

Привет %(user)s,
%(changer)s добавил(а) новый спринт к " +"%(project)s

\n" +"

Спринт %(name)s

\n" +" Просмотреть спринт\n" +"

Команда Тайги

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New sprint created\n" +"Hello %(user)s, %(changer)s has created a new sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Добавлен новый спринт\n" +"Привет %(user)s, %(changer)s добавил(а) новый спринт к %(project)s\n" +"Просмотреть спринт %(name)s можно по ссылке %(url)s\n" +"\n" +"---\n" +"Команда Тайги\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Добавлен спринт \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Спринт удалён

\n" +"

Привет %(user)s,
%(changer)s удалил(а) спринт из %(project)s\n" +"

Спринт %(name)s

\n" +"

Команда Тайги

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Sprint deleted\n" +"Hello %(user)s, %(changer)s has deleted an sprint on %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Спринт удалён\n" +"Привет %(user)s, %(changer)s удалил(а) спринт из %(project)s\n" +"Спринт %(name)s\n" +"\n" +"---\n" +"Команда Тайги\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"[%(project)s] Удалён спринт \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task updated

\n" +"

Hello %(user)s,
%(changer)s has updated a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +" " +msgstr "" +"\n" +"

Задача обновлена

\n" +"

Привет %(user)s,
%(changer)s обновил(а) задачу в %(project)s\n" +"

Задача #%(ref)s %(subject)s

\n" +" Просмотреть задачу\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Task updated\n" +"Hello %(user)s, %(changer)s has updated a task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"Задача обновлена\n" +"Привет %(user)s, %(changer)s обновил(а) задачу в %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Обновлена задача #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New task created

\n" +"

Hello %(user)s,
%(changer)s has created a new task on " +"%(project)s

\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Новая задача создана

\n" +"

Здравствуйте, %(user)s,
%(changer)s создал новую задачу в " +"%(project)s

\n" +"

Задача #%(ref)s %(subject)s

\n" +" Посмотреть задачу\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New task created\n" +"Hello %(user)s, %(changer)s has created a new task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Новая задача создана\n" +"Здравствуйте, %(user)s, %(changer)s создал новую задачу в %(project)s\n" +"Посмотреть задачу #%(ref)s %(subject)s здесь: %(url)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Создана задача #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Задача удалена

\n" +"

Здравствуйте, %(user)s,
%(changer)s удалил задачу для " +"%(project)s

\n" +"

Задача #%(ref)s %(subject)s

\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Task deleted\n" +"Hello %(user)s, %(changer)s has deleted a task on %(project)s\n" +"Task #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Задача удалена\n" +"Здравствуйте, %(user)s, %(changer)s удалил задачу для %(project)s\n" +"Задача #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Удалена задача #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story updated

\n" +"

Hello %(user)s,
%(changer)s has updated a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +" " +msgstr "" +"\n" +"

История от Пользователя изменена

\n" +"

Здравствуйте, %(user)s,
%(changer)s обновил историю для " +"%(project)s

\n" +"

История от Пользователя #%(ref)s %(subject)s

\n" +" Посмотреть историю\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"User story updated\n" +"Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"История от пользователя изменена\n" +"Здравствуйте, %(user)s, %(changer)s обновил историю для %(project)s\n" +"Посмотреть историю от пользователя #%(ref)s %(subject)s здесь: %(url)s\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Изменена ПИ #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New user story created

\n" +"

Hello %(user)s,
%(changer)s has created a new user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Новая история от пользователя создана

\n" +"

Здравствуйте, %(user)s,
%(changer)s создал новую историю для " +"%(project)s

\n" +"

История от Пользователя #%(ref)s %(subject)s

\n" +" Посмотреть историю\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New user story created\n" +"Hello %(user)s, %(changer)s has created a new user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Создана новая История от Пользователя\n" +"Здравствуйте, %(user)s, %(changer)s создал новую историю для %(project)s\n" +"Посмотреть историю от пользователя #%(ref)s %(subject)s здесь: %(url)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Создана ПИ #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

История от Пользователя удалена

\n" +"

Здравствуйте, %(user)s,
%(changer)s удалил историю для " +"%(project)s

\n" +"

История от пользователя #%(ref)s %(subject)s

\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"User Story deleted\n" +"Hello %(user)s, %(changer)s has deleted a user story on %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"История от Пользователя удалена\n" +"Здравствуйте, %(user)s, %(changer)s удалил историю для %(project)s\n" +"История от Пользователя #%(ref)s %(subject)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"[%(project)s] Удалена ПИ #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki Page updated

\n" +"

Hello %(user)s,
%(changer)s has updated a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See Wiki Page\n" +" " +msgstr "" +"\n" +"

Изменена вики-страница

\n" +"

Здравствуйте, %(user)s,
%(changer)s изменил вики-страницу " +"%(project)s

\n" +"

Вики-страница %(page)s

\n" +" Посмотреть вики-страницу\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Wiki Page updated\n" +"\n" +"Hello %(user)s, %(changer)s has updated a wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +msgstr "" +"\n" +"Вики-страница изменена\n" +"\n" +"Здравствуйте, %(user)s, %(changer)s изменил вики-страницу в %(project)s\n" +"\n" +"Просмотреть вики-страницу %(page)s можно по ссылке %(url)s\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Обновлена вики-страница \"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New wiki page created

\n" +"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See " +"wiki page\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Создана новая вики-страница

\n" +"

Здравствуйте, %(user)s,
%(changer)s создал новую вики-страницу " +"для %(project)s

\n" +"

Вики-страница %(page)s

\n" +" Посмотреть вики-страницу\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New wiki page created\n" +"\n" +"Hello %(user)s, %(changer)s has created a new wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Создана новая вики-страница\n" +"\n" +"Здравствуйте, %(user)s, %(changer)s создал новую вики-страницу для " +"%(project)s\n" +"\n" +"Посмотреть вики-страницу %(page)s здесь: %(url)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Создана вики-страница \"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki page deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Вики-страница удалена

\n" +"

Здравствуйте, %(user)s,
%(changer)s удалил вики-страницу для " +"%(project)s

\n" +"

Вики-страница %(page)s

\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Wiki page deleted\n" +"\n" +"Hello %(user)s, %(changer)s has deleted a wiki page on %(project)s\n" +"\n" +"Wiki page %(page)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Вики-страница удалена\n" +"\n" +"Здравствуйте, %(user)s, %(changer)s удалил вики-страницу для %(project)s\n" +"\n" +"Вики-страница %(page)s\n" +"\n" +"---\n" +"Команда Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"[%(project)s] Удалена вики-страница \"%(page)s\"\n" + +#: taiga/projects/notifications/validators.py:44 +msgid "Watchers contains invalid users" +msgstr "наблюдатели содержат неправильных пользователей" + +#: taiga/projects/occ/mixins.py:35 +msgid "The version must be an integer" +msgstr "Версия должна быть целым значением" + +#: taiga/projects/occ/mixins.py:58 +msgid "The version parameter is not valid" +msgstr "Значение версии некорректно" + +#: taiga/projects/occ/mixins.py:74 +msgid "The version doesn't match with the current one" +msgstr "Версия не соответствует текущей" + +#: taiga/projects/occ/mixins.py:93 +msgid "version" +msgstr "версия" + +#: taiga/projects/permissions.py:39 +msgid "You can't leave the project if there are no more owners" +msgstr "Вы не можете покинуть проект если в нём нет других владельцев" + +#: taiga/projects/serializers.py:237 +msgid "Email address is already taken" +msgstr "Этот почтовый адрес уже используется" + +#: taiga/projects/serializers.py:249 +msgid "Invalid role for the project" +msgstr "Неверная роль для этого проекта" + +#: taiga/projects/serializers.py:348 +msgid "Total milestones must be major or equal to zero" +msgstr "Количество вех должно быть больше или равно нулю" + +#: taiga/projects/serializers.py:405 +msgid "Default options" +msgstr "Параметры по умолчанию" + +#: taiga/projects/serializers.py:406 +msgid "User story's statuses" +msgstr "Статусу пользовательских историй" + +#: taiga/projects/serializers.py:407 +msgid "Points" +msgstr "Очки" + +#: taiga/projects/serializers.py:408 +msgid "Task's statuses" +msgstr "Статусы задачи" + +#: taiga/projects/serializers.py:409 +msgid "Issue's statuses" +msgstr "Статусы проблемы" + +#: taiga/projects/serializers.py:410 +msgid "Issue's types" +msgstr "Типы проблемы" + +#: taiga/projects/serializers.py:411 +msgid "Priorities" +msgstr "Приоритеты" + +#: taiga/projects/serializers.py:412 +msgid "Severities" +msgstr "Степени важности" + +#: taiga/projects/serializers.py:413 +msgid "Roles" +msgstr "Роли" + +#: taiga/projects/services/stats.py:72 +msgid "Future sprint" +msgstr "Будущий спринт" + +#: taiga/projects/services/stats.py:89 +msgid "Project End" +msgstr "Окончание проекта" + +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +msgid "You don't have permissions to set this sprint to this task." +msgstr "У вас нет прав, чтобы назначить этот спринт для этой задачи." + +#: taiga/projects/tasks/api.py:97 +msgid "You don't have permissions to set this user story to this task." +msgstr "" +"У вас нет прав, чтобы назначить эту историю от пользователя этой задаче." + +#: taiga/projects/tasks/api.py:100 +msgid "You don't have permissions to set this status to this task." +msgstr "У вас нет прав, чтобы установить этот статус для этой задачи." + +#: taiga/projects/tasks/models.py:56 +msgid "us order" +msgstr "порядок ПИ" + +#: taiga/projects/tasks/models.py:58 +msgid "taskboard order" +msgstr "порядок панели задач" + +#: taiga/projects/tasks/models.py:66 +msgid "is iocaine" +msgstr "- иокаин" + +#: taiga/projects/tasks/validators.py:12 +msgid "There's no task with that id" +msgstr "Нет задачи с таким идентификатором" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:6 +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:4 +msgid "someone" +msgstr "некто" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:11 +#, python-format +msgid "" +"\n" +"

You have been invited to Taiga!

\n" +"

Hi! %(full_name)s has sent you an invitation to join project " +"%(project)s in Taiga.
Taiga is a Free, open Source Agile Project " +"Management Tool.

\n" +" " +msgstr "" +"\n" +"

Вас пригласили в Taiga!

\n" +"

Привет! %(full_name)s пригласил вас присоединиться к проекту " +"%(project)s в Taiga.
Taiga - это бесплатный инструмент с открытым " +"исходным кодом для управления проектами в стиле Agile.

\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 +#, python-format +msgid "" +"\n" +"

And now a few words from the jolly good fellow or sistren
" +"who thought so kindly as to invite you

\n" +"

%(extra)s

\n" +" " +msgstr "" +"\n" +"

А теперь несколько слов от добрых братьев или сестёр,
" +"которые были столь любезны, что пригласили вас

\n" +"

%(extra)s

\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation to Taiga" +msgstr "Принять приглашение в Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation" +msgstr "Принять приглашение" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:25 +msgid "The Taiga Team" +msgstr "Команда Тайги" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:6 +#, python-format +msgid "" +"\n" +"You, or someone you know, has invited you to Taiga\n" +"\n" +"Hi! %(full_name)s has sent you an invitation to join a project called " +"%(project)s which is being managed on Taiga, a Free, open Source Agile " +"Project Management Tool.\n" +msgstr "" +"\n" +"Вы, или кто-то, кого вы знаете, пригласили вас в Taiga\n" +"\n" +"Привет! %(full_name)s пригласили вас поучаствовать в проекте %(project)s, " +"который управляется в Taiga - бесплатном инструменте с открытым исходным " +"кодом для управления проектами в стиле Agile.\n" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 +#, python-format +msgid "" +"\n" +"And now a few words from the jolly good fellow or sistren who thought so " +"kindly as to invite you:\n" +"\n" +"%(extra)s\n" +" " +msgstr "" +"\n" +"А теперь несколько слов от добрых братьев или сестёр, которые были столь " +"любезны, что пригласили вас:\n" +"\n" +"%(extra)s\n" +" " + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:18 +msgid "Accept your invitation to Taiga following this link:" +msgstr "Принять приглашение в Тайгу можно перейдя по ссылке:" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:20 +msgid "" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"---\n" +"Команда Тайги\n" + +#: taiga/projects/templates/emails/membership_invitation-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Invitation to join to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] Приглашение присоединиться к проекту '%(project)s'\n" + +#: taiga/projects/templates/emails/membership_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

You have been added to a project

\n" +"

Hello %(full_name)s,
you have been added to the project " +"%(project)s

\n" +" Go to " +"project\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Вы были добавлены в проект

\n" +"

Здравствуйте, %(full_name)s,
вы были добавлены в проект " +"%(project)s

\n" +" Перейти к проекту\n" +"

Команда Taiga

\n" +" " + +#: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"You have been added to a project\n" +"Hello %(full_name)s,you have been added to the project %(project)s\n" +"\n" +"See project at %(url)s\n" +msgstr "" +"\n" +"Вы были добавлены в проект\n" +"Здравствуйте, %(full_name)s, вы были добавлены в проект %(project)s\n" +"\n" +"Посмотреть проект можно здесь: %(url)s\n" + +#: taiga/projects/templates/emails/membership_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Added to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] Добавлены к проекту '%(project)s'\n" + +#. Translators: Name of scrum project template. +#: taiga/projects/translations.py:28 +msgid "Scrum" +msgstr "" + +#. Translators: Description of scrum project template. +#: taiga/projects/translations.py:30 +msgid "" +"The agile product backlog in Scrum is a prioritized features list, " +"containing short descriptions of all functionality desired in the product. " +"When applying Scrum, it's not necessary to start a project with a lengthy, " +"upfront effort to document all requirements. The Scrum product backlog is " +"then allowed to grow and change as more is learned about the product and its " +"customers" +msgstr "" + +#. Translators: Name of kanban project template. +#: taiga/projects/translations.py:33 +msgid "Kanban" +msgstr "" + +#. Translators: Description of kanban project template. +#: taiga/projects/translations.py:35 +msgid "" +"Kanban is a method for managing knowledge work with an emphasis on just-in-" +"time delivery while not overloading the team members. In this approach, the " +"process, from definition of a task to its delivery to the customer, is " +"displayed for participants to see and team members pull work from a queue." +msgstr "" + +#. Translators: User story point value (value = undefined) +#: taiga/projects/translations.py:43 +msgid "?" +msgstr "" + +#. Translators: User story point value (value = 0) +#: taiga/projects/translations.py:45 +msgid "0" +msgstr "" + +#. Translators: User story point value (value = 0.5) +#: taiga/projects/translations.py:47 +msgid "1/2" +msgstr "" + +#. Translators: User story point value (value = 1) +#: taiga/projects/translations.py:49 +msgid "1" +msgstr "" + +#. Translators: User story point value (value = 2) +#: taiga/projects/translations.py:51 +msgid "2" +msgstr "" + +#. Translators: User story point value (value = 3) +#: taiga/projects/translations.py:53 +msgid "3" +msgstr "" + +#. Translators: User story point value (value = 5) +#: taiga/projects/translations.py:55 +msgid "5" +msgstr "" + +#. Translators: User story point value (value = 8) +#: taiga/projects/translations.py:57 +msgid "8" +msgstr "" + +#. Translators: User story point value (value = 10) +#: taiga/projects/translations.py:59 +msgid "10" +msgstr "" + +#. Translators: User story point value (value = 13) +#: taiga/projects/translations.py:61 +msgid "13" +msgstr "" + +#. Translators: User story point value (value = 20) +#: taiga/projects/translations.py:63 +msgid "20" +msgstr "" + +#. Translators: User story point value (value = 40) +#: taiga/projects/translations.py:65 +msgid "40" +msgstr "" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:73 taiga/projects/translations.py:96 +#: taiga/projects/translations.py:112 +msgid "New" +msgstr "" + +#. Translators: User story status +#: taiga/projects/translations.py:76 +msgid "Ready" +msgstr "" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:79 taiga/projects/translations.py:98 +#: taiga/projects/translations.py:114 +msgid "In progress" +msgstr "" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:82 taiga/projects/translations.py:100 +#: taiga/projects/translations.py:116 +msgid "Ready for test" +msgstr "" + +#. Translators: User story status +#: taiga/projects/translations.py:85 +msgid "Done" +msgstr "" + +#. Translators: User story status +#: taiga/projects/translations.py:88 +msgid "Archived" +msgstr "" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:102 taiga/projects/translations.py:118 +msgid "Closed" +msgstr "" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:104 taiga/projects/translations.py:120 +msgid "Needs Info" +msgstr "" + +#. Translators: Issue status +#: taiga/projects/translations.py:122 +msgid "Postponed" +msgstr "" + +#. Translators: Issue status +#: taiga/projects/translations.py:124 +msgid "Rejected" +msgstr "" + +#. Translators: Issue type +#: taiga/projects/translations.py:132 +msgid "Bug" +msgstr "" + +#. Translators: Issue type +#: taiga/projects/translations.py:134 +msgid "Question" +msgstr "" + +#. Translators: Issue type +#: taiga/projects/translations.py:136 +msgid "Enhancement" +msgstr "" + +#. Translators: Issue priority +#: taiga/projects/translations.py:144 +msgid "Low" +msgstr "" + +#. Translators: Issue priority +#. Translators: Issue severity +#: taiga/projects/translations.py:146 taiga/projects/translations.py:159 +msgid "Normal" +msgstr "" + +#. Translators: Issue priority +#: taiga/projects/translations.py:148 +msgid "High" +msgstr "" + +#. Translators: Issue severity +#: taiga/projects/translations.py:155 +msgid "Wishlist" +msgstr "" + +#. Translators: Issue severity +#: taiga/projects/translations.py:157 +msgid "Minor" +msgstr "" + +#. Translators: Issue severity +#: taiga/projects/translations.py:161 +msgid "Important" +msgstr "" + +#. Translators: Issue severity +#: taiga/projects/translations.py:163 +msgid "Critical" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:170 +msgid "UX" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:172 +msgid "Design" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:174 +msgid "Front" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:176 +msgid "Back" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:178 +msgid "Product Owner" +msgstr "" + +#. Translators: User role +#: taiga/projects/translations.py:180 +msgid "Stakeholder" +msgstr "" + +#: taiga/projects/userstories/api.py:150 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:154 +msgid "You don't have permissions to set this status to this user story." +msgstr "" + +#: taiga/projects/userstories/api.py:248 +#, python-brace-format +msgid "" +"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " +"{subject}\")" +msgstr "" + +#: taiga/projects/userstories/models.py:37 +msgid "role" +msgstr "" + +#: taiga/projects/userstories/models.py:75 +msgid "backlog order" +msgstr "" + +#: taiga/projects/userstories/models.py:77 +#: taiga/projects/userstories/models.py:79 +msgid "sprint order" +msgstr "" + +#: taiga/projects/userstories/models.py:87 +msgid "finish date" +msgstr "" + +#: taiga/projects/userstories/models.py:95 +msgid "is client requirement" +msgstr "" + +#: taiga/projects/userstories/models.py:97 +msgid "is team requirement" +msgstr "" + +#: taiga/projects/userstories/models.py:102 +msgid "generated from issue" +msgstr "" + +#: taiga/projects/userstories/validators.py:28 +msgid "There's no user story with that id" +msgstr "" + +#: taiga/projects/validators.py:28 +msgid "There's no project with that id" +msgstr "" + +#: taiga/projects/validators.py:37 +msgid "There's no user story status with that id" +msgstr "" + +#: taiga/projects/validators.py:46 +msgid "There's no task status with that id" +msgstr "" + +#: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 +#: taiga/projects/votes/models.py:54 +msgid "Votes" +msgstr "" + +#: taiga/projects/votes/models.py:50 +msgid "votes" +msgstr "" + +#: taiga/projects/votes/models.py:53 +msgid "Vote" +msgstr "" + +#: taiga/projects/wiki/api.py:60 +msgid "'content' parameter is mandatory" +msgstr "" + +#: taiga/projects/wiki/api.py:63 +msgid "'project_id' parameter is mandatory" +msgstr "" + +#: taiga/projects/wiki/models.py:36 +msgid "last modifier" +msgstr "" + +#: taiga/projects/wiki/models.py:69 +msgid "href" +msgstr "" + +#: taiga/users/admin.py:50 +msgid "Personal info" +msgstr "" + +#: taiga/users/admin.py:52 +msgid "Permissions" +msgstr "" + +#: taiga/users/admin.py:53 +msgid "Important dates" +msgstr "" + +#: taiga/users/api.py:124 taiga/users/api.py:131 +msgid "Invalid username or email" +msgstr "" + +#: taiga/users/api.py:140 +msgid "Mail sended successful!" +msgstr "" + +#: taiga/users/api.py:152 taiga/users/api.py:157 +msgid "Token is invalid" +msgstr "" + +#: taiga/users/api.py:178 +msgid "Current password parameter needed" +msgstr "" + +#: taiga/users/api.py:181 +msgid "New password parameter needed" +msgstr "" + +#: taiga/users/api.py:184 +msgid "Invalid password length at least 6 charaters needed" +msgstr "" + +#: taiga/users/api.py:187 +msgid "Invalid current password" +msgstr "" + +#: taiga/users/api.py:203 +msgid "Incomplete arguments" +msgstr "" + +#: taiga/users/api.py:208 +msgid "Invalid image format" +msgstr "" + +#: taiga/users/api.py:261 +msgid "Duplicated email" +msgstr "" + +#: taiga/users/api.py:263 +msgid "Not valid email" +msgstr "" + +#: taiga/users/api.py:283 taiga/users/api.py:289 +msgid "" +"Invalid, are you sure the token is correct and you didn't use it before?" +msgstr "" + +#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +msgid "Invalid, are you sure the token is correct?" +msgstr "" + +#: taiga/users/models.py:69 +msgid "superuser status" +msgstr "" + +#: taiga/users/models.py:70 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" + +#: taiga/users/models.py:100 +msgid "username" +msgstr "" + +#: taiga/users/models.py:101 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" +msgstr "" + +#: taiga/users/models.py:104 +msgid "Enter a valid username." +msgstr "" + +#: taiga/users/models.py:107 +msgid "active" +msgstr "" + +#: taiga/users/models.py:108 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "" + +#: taiga/users/models.py:114 +msgid "biography" +msgstr "" + +#: taiga/users/models.py:117 +msgid "photo" +msgstr "" + +#: taiga/users/models.py:118 +msgid "date joined" +msgstr "" + +#: taiga/users/models.py:120 +msgid "default language" +msgstr "" + +#: taiga/users/models.py:122 +msgid "default theme" +msgstr "" + +#: taiga/users/models.py:124 +msgid "default timezone" +msgstr "" + +#: taiga/users/models.py:126 +msgid "colorize tags" +msgstr "" + +#: taiga/users/models.py:131 +msgid "email token" +msgstr "" + +#: taiga/users/models.py:133 +msgid "new email address" +msgstr "" + +#: taiga/users/models.py:188 +msgid "permissions" +msgstr "" + +#: taiga/users/serializers.py:59 +msgid "invalid" +msgstr "" + +#: taiga/users/serializers.py:70 +msgid "Invalid username. Try with a different one." +msgstr "" + +#: taiga/users/services.py:48 taiga/users/services.py:52 +msgid "Username or password does not matches user." +msgstr "" + +#: taiga/users/templates/emails/change_email-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Change your email

\n" +"

Hello %(full_name)s,
please confirm your email

\n" +" Confirm " +"email\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" + +#: taiga/users/templates/emails/change_email-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, please confirm your email\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" + +#: taiga/users/templates/emails/change_email-subject.jinja:1 +msgid "[Taiga] Change email" +msgstr "" + +#: taiga/users/templates/emails/password_recovery-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Recover your password

\n" +"

Hello %(full_name)s,
you asked to recover your password

\n" +" Recover your password\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" + +#: taiga/users/templates/emails/password_recovery-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, you asked to recover your password\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" + +#: taiga/users/templates/emails/password_recovery-subject.jinja:1 +msgid "[Taiga] Password recovery" +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:6 +msgid "" +"\n" +" \n" +"

Thank you for registering in Taiga

\n" +"

We hope you enjoy it

\n" +"

We built Taiga because we wanted the project management tool " +"that sits open on our computers all day long, to serve as a continued " +"reminder of why we love to collaborate, code and design.

\n" +"

We built it to be beautiful, elegant, simple to use and fun - " +"without forsaking flexibility and power.

\n" +" The taiga Team\n" +" \n" +" " +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:23 +#, python-format +msgid "" +"\n" +" You may remove your account from this service clicking " +"here\n" +" " +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:1 +msgid "" +"\n" +"Thank you for registering in Taiga\n" +"\n" +"We hope you enjoy it\n" +"\n" +"We built Taiga because we wanted the project management tool that sits open " +"on our computers all day long, to serve as a continued reminder of why we " +"love to collaborate, code and design.\n" +"\n" +"We built it to be beautiful, elegant, simple to use and fun - without " +"forsaking flexibility and power.\n" +"\n" +"--\n" +"The taiga Team\n" +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:13 +#, python-format +msgid "" +"\n" +"You may remove your account from this service: %(url)s\n" +msgstr "" + +#: taiga/users/templates/emails/registered_user-subject.jinja:1 +msgid "You've been Taigatized!" +msgstr "" + +#: taiga/users/validators.py:29 +msgid "There's no role with that id" +msgstr "" + +#: taiga/userstorage/api.py:50 +msgid "" +"Duplicate key value violates unique constraint. Key '{}' already exists." +msgstr "" + +#: taiga/userstorage/models.py:30 +msgid "key" +msgstr "" + +#: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 +msgid "URL" +msgstr "" + +#: taiga/webhooks/models.py:29 +msgid "secret key" +msgstr "" + +#: taiga/webhooks/models.py:39 +msgid "status code" +msgstr "" + +#: taiga/webhooks/models.py:40 +msgid "request data" +msgstr "данные запроса" + +#: taiga/webhooks/models.py:41 +msgid "request headers" +msgstr "заголовки запроса" + +#: taiga/webhooks/models.py:42 +msgid "response data" +msgstr "данные ответа" + +#: taiga/webhooks/models.py:43 +msgid "response headers" +msgstr "заголовки ответа" + +#: taiga/webhooks/models.py:44 +msgid "duration" +msgstr "длительность" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 7028e182..2223881c 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-07-06 16:06+0200\n" +"POT-Creation-Date: 2015-08-03 11:09+0200\n" "PO-Revision-Date: 2015-08-01 03:51+0000\n" "Last-Translator: Chi-Hsun Tsai \n" "Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" @@ -327,12 +327,12 @@ msgstr "因錯誤或無效參數,一致性出錯" msgid "Precondition error" msgstr "前提出錯" -#: taiga/base/filters.py:74 +#: taiga/base/filters.py:79 msgid "Error in filter params types." msgstr "過濾參數類型出錯" -#: taiga/base/filters.py:121 taiga/base/filters.py:210 -#: taiga/base/filters.py:259 +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 msgid "'project' must be an integer value." msgstr "專案須為整數值" @@ -895,8 +895,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "載荷為無效json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:175 -#: taiga/projects/tasks/api.py:74 taiga/projects/userstories/api.py:87 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 +#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 msgid "The project doesn't exist" msgstr "專案不存在" @@ -1237,19 +1237,19 @@ msgstr "管理員專案數值" msgid "Admin roles" msgstr "管理員角色" -#: taiga/projects/api.py:204 +#: taiga/projects/api.py:198 msgid "Not valid template name" msgstr "非有效樣板名稱 " -#: taiga/projects/api.py:207 +#: taiga/projects/api.py:201 msgid "Not valid template description" msgstr "無效樣板描述" -#: taiga/projects/api.py:475 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 msgid "At least one of the user must be an active admin" msgstr "至少需有一位使用者擔任管理員" -#: taiga/projects/api.py:505 +#: taiga/projects/api.py:499 msgid "You don't have permisions to see that." msgstr "您無觀看權限" @@ -1519,23 +1519,23 @@ msgstr "封鎖筆記" msgid "sprint" msgstr "衝刺任務" -#: taiga/projects/issues/api.py:195 +#: taiga/projects/issues/api.py:154 msgid "You don't have permissions to set this sprint to this issue." msgstr "您無權限設定此問題的衝刺任務" -#: taiga/projects/issues/api.py:199 +#: taiga/projects/issues/api.py:158 msgid "You don't have permissions to set this status to this issue." msgstr "您無權限設定此問題的狀態" -#: taiga/projects/issues/api.py:203 +#: taiga/projects/issues/api.py:162 msgid "You don't have permissions to set this severity to this issue." msgstr "您無權限設定此問題的嚴重性" -#: taiga/projects/issues/api.py:207 +#: taiga/projects/issues/api.py:166 msgid "You don't have permissions to set this priority to this issue." msgstr "您無權限設定此問題的優先性" -#: taiga/projects/issues/api.py:211 +#: taiga/projects/issues/api.py:170 msgid "You don't have permissions to set this type to this issue." msgstr "您無權限設定此問題的類型" @@ -2636,15 +2636,15 @@ msgstr "未來之衝刺" msgid "Project End" msgstr "專案結束" -#: taiga/projects/tasks/api.py:90 taiga/projects/tasks/api.py:99 +#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this sprint to this task." msgstr "無權限更動此任務下的衝刺任務" -#: taiga/projects/tasks/api.py:93 +#: taiga/projects/tasks/api.py:97 msgid "You don't have permissions to set this user story to this task." msgstr "無權限更動此務下的使用者故事" -#: taiga/projects/tasks/api.py:96 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this status to this task." msgstr "無權限更動此任務下的狀態" @@ -3046,15 +3046,15 @@ msgstr "產品所有人" msgid "Stakeholder" msgstr "利害關係人" -#: taiga/projects/userstories/api.py:134 +#: taiga/projects/userstories/api.py:150 msgid "You don't have permissions to set this sprint to this user story." msgstr "無權限更動使用者故事的衝刺任務" -#: taiga/projects/userstories/api.py:138 +#: taiga/projects/userstories/api.py:154 msgid "You don't have permissions to set this status to this user story." msgstr "無權限更動此使用者故事的狀態" -#: taiga/projects/userstories/api.py:212 +#: taiga/projects/userstories/api.py:248 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " From 0adc0ebf9e02522fa782372fd609f588e811bf81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 3 Aug 2015 14:38:43 +0200 Subject: [PATCH 086/190] Return an empty list even in bad searchs --- taiga/searches/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taiga/searches/api.py b/taiga/searches/api.py index befe539b..9fec9bb8 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -56,11 +56,12 @@ class SearchViewSet(viewsets.ViewSet): futures_list.append(wiki_pages_future) for future in futures.as_completed(futures_list): + data = [] try: data = future.result() except Exception as exc: print('%s generated an exception: %s' % (future.result_key, exc)) - else: + finally: result[future.result_key] = data result["count"] = sum(map(lambda x: len(x), result.values())) From a251761b1800c0f4105d756a44e39f5764cd6f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 3 Aug 2015 14:44:08 +0200 Subject: [PATCH 087/190] Revert "Add support for partial words on global searches" This reverts commit 9cbecd9b7edf82749761361454827575a0a19586. --- taiga/searches/services.py | 14 +++++--------- tests/integration/test_searches.py | 20 +++----------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/taiga/searches/services.py b/taiga/searches/services.py index ccea5c19..9d7d0529 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -25,10 +25,9 @@ def search_user_stories(project, text): model_cls = apps.get_model("userstories", "UserStory") where_clause = ("to_tsvector('simple', coalesce(userstories_userstory.subject, '') || ' ' || " "coalesce(userstories_userstory.ref) || ' ' || " - "coalesce(userstories_userstory.description, '')) @@ to_tsquery(%s)") + "coalesce(userstories_userstory.description, '')) @@ plainto_tsquery(%s)") if text: - text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -39,10 +38,9 @@ def search_tasks(project, text): model_cls = apps.get_model("tasks", "Task") where_clause = ("to_tsvector('simple', coalesce(tasks_task.subject, '') || ' ' || " "coalesce(tasks_task.ref) || ' ' || " - "coalesce(tasks_task.description, '')) @@ to_tsquery(%s)") + "coalesce(tasks_task.description, '')) @@ plainto_tsquery(%s)") if text: - text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -53,10 +51,9 @@ def search_issues(project, text): model_cls = apps.get_model("issues", "Issue") where_clause = ("to_tsvector('simple', coalesce(issues_issue.subject) || ' ' || " "coalesce(issues_issue.ref) || ' ' || " - "coalesce(issues_issue.description)) @@ to_tsquery(%s)") + "coalesce(issues_issue.description, '')) @@ plainto_tsquery(%s)") if text: - text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) @@ -65,11 +62,10 @@ def search_issues(project, text): def search_wiki_pages(project, text): model_cls = apps.get_model("wiki", "WikiPage") - where_clause = ("to_tsvector('simple', coalesce(wiki_wikipage.slug) || ' ' || coalesce(wiki_wikipage.content)) " - "@@ to_tsquery(%s)") + where_clause = ("to_tsvector('simple', coalesce(wiki_wikipage.slug) || ' ' || " + "coalesce(wiki_wikipage.content, '')) @@ plainto_tsquery(%s)") if text: - text += ":*" return (model_cls.objects.extra(where=[where_clause], params=[text]) .filter(project_id=project.pk)[:MAX_RESULTS]) diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index f6a26cc1..ec6743ad 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -74,11 +74,11 @@ def searches_initial_data(): m.tsk2 = f.TaskFactory.create(project=m.project1) m.tsk3 = f.TaskFactory.create(project=m.project1, subject="Back to the future") - m.iss1 = f.IssueFactory.create(project=m.project1, subject="Design and Frontend") + m.iss1 = f.IssueFactory.create(project=m.project1, subject="Backend and Frontend") m.iss2 = f.IssueFactory.create(project=m.project2) - m.iss3 = f.IssueFactory.create(project=m.project1, subject="Green Frog") + m.iss3 = f.IssueFactory.create(project=m.project1) - m.wiki1 = f.WikiPageFactory.create(project=m.project1, content="Final Frontier") + m.wiki1 = f.WikiPageFactory.create(project=m.project1) m.wiki2 = f.WikiPageFactory.create(project=m.project1, content="Frontend, future") m.wiki3 = f.WikiPageFactory.create(project=m.project2) @@ -131,20 +131,6 @@ def test_search_text_query_in_my_project(client, searches_initial_data): assert len(response.data["wikipages"]) == 0 -def test_search_partial_text_query_in_my_project(client, searches_initial_data): - data = searches_initial_data - - client.login(data.member1.user) - - response = client.get(reverse("search-list"), {"project": data.project1.id, "text": "fron"}) - assert response.status_code == 200 - assert response.data["count"] == 3 - assert len(response.data["userstories"]) == 0 - assert len(response.data["tasks"]) == 0 - assert len(response.data["issues"]) == 1 - assert len(response.data["wikipages"]) == 2 - - def test_search_text_query_with_an_invalid_project_id(client, searches_initial_data): data = searches_initial_data From 84f517559a99446be61e1af1f6952431fd9d76a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 3 Aug 2015 16:57:22 +0200 Subject: [PATCH 088/190] Revert "Add full-text index to the search app" This reverts commit b4a30f64afbedb2ff9cab3b60d800c5509457627. --- taiga/searches/migrations/0001_initial.py | 41 ----------------------- taiga/searches/migrations/__init__.py | 0 taiga/searches/services.py | 16 +++++---- 3 files changed, 9 insertions(+), 48 deletions(-) delete mode 100644 taiga/searches/migrations/0001_initial.py delete mode 100644 taiga/searches/migrations/__init__.py diff --git a/taiga/searches/migrations/0001_initial.py b/taiga/searches/migrations/0001_initial.py deleted file mode 100644 index b30cfa4d..00000000 --- a/taiga/searches/migrations/0001_initial.py +++ /dev/null @@ -1,41 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('wiki', '0001_initial'), - ('userstories', '0009_remove_userstory_is_archived'), - ('issues', '0005_auto_20150623_1923'), - ('tasks', '0006_auto_20150623_1923'), - ] - - operations = [ - migrations.RunSQL( - """ - CREATE INDEX "userstories_full_text_idx" ON userstories_userstory USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); - """, - reverse_sql="""DROP INDEX IF EXISTS "userstories_full_text_idx";""" - ), - migrations.RunSQL( - """ - CREATE INDEX "tasks_full_text_idx" ON tasks_task USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); - """, - reverse_sql="""DROP INDEX IF EXISTS "tasks_full_text_idx";""" - ), - migrations.RunSQL( - """ - CREATE INDEX "issues_full_text_idx" ON issues_issue USING gin(to_tsvector('simple', coalesce(subject, '') || ' ' || coalesce(ref) || ' ' || coalesce(description, ''))); - """, - reverse_sql="""DROP INDEX IF EXISTS "issues_full_text_idx";""" - ), - migrations.RunSQL( - """ - CREATE INDEX "wikipages_full_text_idx" ON wiki_wikipage USING gin(to_tsvector('simple', coalesce(slug, '') || ' ' || coalesce(content, ''))); - """, - reverse_sql="""DROP INDEX IF EXISTS "wikipages_full_text_idx";""" - ), - ] diff --git a/taiga/searches/migrations/__init__.py b/taiga/searches/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/taiga/searches/services.py b/taiga/searches/services.py index 9d7d0529..495e298d 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -23,9 +23,10 @@ MAX_RESULTS = getattr(settings, "SEARCHES_MAX_RESULTS", 150) def search_user_stories(project, text): model_cls = apps.get_model("userstories", "UserStory") - where_clause = ("to_tsvector('simple', coalesce(userstories_userstory.subject, '') || ' ' || " - "coalesce(userstories_userstory.ref) || ' ' || " - "coalesce(userstories_userstory.description, '')) @@ plainto_tsquery(%s)") + where_clause = ("to_tsvector(coalesce(userstories_userstory.subject) || ' ' || " + "coalesce(userstories_userstory.ref) || ' ' || " + "coalesce(userstories_userstory.description, '')) " + "@@ plainto_tsquery(%s)") if text: return (model_cls.objects.extra(where=[where_clause], params=[text]) @@ -36,7 +37,7 @@ def search_user_stories(project, text): def search_tasks(project, text): model_cls = apps.get_model("tasks", "Task") - where_clause = ("to_tsvector('simple', coalesce(tasks_task.subject, '') || ' ' || " + where_clause = ("to_tsvector(coalesce(tasks_task.subject, '') || ' ' || " "coalesce(tasks_task.ref) || ' ' || " "coalesce(tasks_task.description, '')) @@ plainto_tsquery(%s)") @@ -49,7 +50,7 @@ def search_tasks(project, text): def search_issues(project, text): model_cls = apps.get_model("issues", "Issue") - where_clause = ("to_tsvector('simple', coalesce(issues_issue.subject) || ' ' || " + where_clause = ("to_tsvector(coalesce(issues_issue.subject) || ' ' || " "coalesce(issues_issue.ref) || ' ' || " "coalesce(issues_issue.description, '')) @@ plainto_tsquery(%s)") @@ -62,8 +63,9 @@ def search_issues(project, text): def search_wiki_pages(project, text): model_cls = apps.get_model("wiki", "WikiPage") - where_clause = ("to_tsvector('simple', coalesce(wiki_wikipage.slug) || ' ' || " - "coalesce(wiki_wikipage.content, '')) @@ plainto_tsquery(%s)") + where_clause = ("to_tsvector(coalesce(wiki_wikipage.slug) || ' ' || " + "coalesce(wiki_wikipage.content, '')) " + "@@ plainto_tsquery(%s)") if text: return (model_cls.objects.extra(where=[where_clause], params=[text]) From 340f2442b3dd6141a2a4407488be5a78009cac0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 6 Aug 2015 17:11:15 +0200 Subject: [PATCH 089/190] Add logging to export_import exceptions --- settings/common.py | 5 +++++ taiga/export_import/tasks.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/settings/common.py b/settings/common.py index 26ee9a51..9b8cfcec 100644 --- a/settings/common.py +++ b/settings/common.py @@ -346,6 +346,11 @@ LOGGING = { "level": "ERROR", "propagate": False, }, + "taiga.export_import": { + "handlers": ["mail_admins", "console"], + "level": "ERROR", + "propagate": False, + }, "taiga": { "handlers": ["console"], "level": "DEBUG", diff --git a/taiga/export_import/tasks.py b/taiga/export_import/tasks.py index 9ac5b42f..1698b9c4 100644 --- a/taiga/export_import/tasks.py +++ b/taiga/export_import/tasks.py @@ -15,10 +15,13 @@ # along with this program. If not, see . import datetime +import logging +import sys from django.core.files.storage import default_storage from django.core.files.base import ContentFile from django.utils import timezone + from django.conf import settings from django.utils.translation import ugettext as _ @@ -30,6 +33,8 @@ from .service import project_to_dict from .dump_service import dict_to_project from .renderers import ExportRenderer +logger = logging.getLogger('taiga.export_import') + @app.task(bind=True) def dump_project(self, user, project): @@ -52,6 +57,7 @@ def dump_project(self, user, project): } email = mbuilder.export_error(user, ctx) email.send() + logger.error('Error generating dump %s (by %s)', project.slug, user, exc_info=sys.exc_info()) return deletion_date = timezone.now() + datetime.timedelta(seconds=settings.EXPORTS_TTL) @@ -84,6 +90,7 @@ def load_project_dump(user, dump): } email = mbuilder.import_error(user, ctx) email.send() + logger.error('Error loading dump %s (by %s)', project.slug, user, exc_info=sys.exc_info()) return ctx = {"user": user, "project": project} From 32c6452bfa3e65b05d1e204bdcd8666f4411a32a Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 3 Aug 2015 10:19:54 +0200 Subject: [PATCH 090/190] Removing descriptions from list API --- taiga/projects/issues/api.py | 3 +++ taiga/projects/issues/serializers.py | 7 +++++++ taiga/projects/tasks/api.py | 3 +++ taiga/projects/tasks/serializers.py | 7 +++++++ taiga/projects/userstories/api.py | 3 +++ taiga/projects/userstories/serializers.py | 8 ++++++++ taiga/timeline/signals.py | 14 +++++++++++++- 7 files changed, 44 insertions(+), 1 deletion(-) diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index 7a1033b6..ee587e8c 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -79,6 +79,9 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, if self.action in ["retrieve", "by_ref"]: return serializers.IssueNeighborsSerializer + if self.action == "list": + return serializers.IssueListSerializer + return serializers.IssueSerializer def update(self, request, *args, **kwargs): diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index 77b22bc5..2b2723dd 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -64,6 +64,13 @@ class IssueSerializer(WatchersValidator, serializers.ModelSerializer): return getattr(obj, "votes_count", 0) +class IssueListSerializer(IssueSerializer): + class Meta: + model = models.Issue + read_only_fields = ('id', 'ref', 'created_date', 'modified_date') + exclude=("description", "description_html") + + class IssueNeighborsSerializer(NeighborsSerializerMixin, IssueSerializer): def serialize_neighbor(self, neighbor): return NeighborIssueSerializer(neighbor).data diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 7b6b90ac..702d0a58 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -46,6 +46,9 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, if self.action in ["retrieve", "by_ref"]: return serializers.TaskNeighborsSerializer + if self.action == "list": + return serializers.TaskListSerializer + return serializers.TaskSerializer def update(self, request, *args, **kwargs): diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 131bb75b..6f64e2d5 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -67,6 +67,13 @@ class TaskSerializer(WatchersValidator, serializers.ModelSerializer): return obj.status.is_closed +class TaskListSerializer(TaskSerializer): + class Meta: + model = models.Task + read_only_fields = ('id', 'ref', 'created_date', 'modified_date') + exclude=("description", "description_html") + + class TaskNeighborsSerializer(NeighborsSerializerMixin, TaskSerializer): def serialize_neighbor(self, neighbor): return NeighborTaskSerializer(neighbor).data diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index e3d7f986..4f065547 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -76,6 +76,9 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi if self.action in ["retrieve", "by_ref"]: return serializers.UserStoryNeighborsSerializer + if self.action == "list": + return serializers.UserStoryListSerializer + return serializers.UserStorySerializer def update(self, request, *args, **kwargs): diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 1773776f..a23cd63c 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -97,6 +97,14 @@ class UserStorySerializer(WatchersValidator, serializers.ModelSerializer): return mdrender(obj.project, obj.description) +class UserStoryListSerializer(UserStorySerializer): + class Meta: + model = models.UserStory + depth = 0 + read_only_fields = ('created_date', 'modified_date') + exclude=("description", "description_html") + + class UserStoryNeighborsSerializer(NeighborsSerializerMixin, UserStorySerializer): def serialize_neighbor(self, neighbor): return NeighborUserStorySerializer(neighbor).data diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index dc8b5a2d..7769817d 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -16,6 +16,7 @@ from django.conf import settings from django.utils import timezone +from django.utils.translation import ugettext as _ from taiga.projects.history import services as history_services from taiga.projects.models import Project @@ -79,7 +80,16 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d extra_data=extra_data) +def _clean_description_fields(values_diff): + # Description_diff and description_html if included can be huge, we are + # removing the html one and clearing the diff + values_diff.pop("description_html", None) + if "description_diff" in values_diff: + values_diff["description_diff"] = _("Check the history API for the exact diff") + + def on_new_history_entry(sender, instance, created, **kwargs): + if instance._importing: return @@ -99,9 +109,11 @@ def on_new_history_entry(sender, instance, created, **kwargs): event_type = "delete" user = User.objects.get(id=instance.user["pk"]) + values_diff = instance.values_diff + _clean_description_fields(values_diff) extra_data = { - "values_diff": instance.values_diff, + "values_diff": values_diff, "user": extract_user_info(user), "comment": instance.comment, "comment_html": instance.comment_html, From a744146af9d37ff2b9fccbe431928ba49340c886 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Sat, 8 Aug 2015 14:50:36 +0200 Subject: [PATCH 091/190] Fix membership timeline entries in the user profile --- taiga/timeline/service.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index 28327297..a86965a1 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -122,7 +122,9 @@ def filter_timeline_for_user(timeline, user): # There is no specific permission for seeing new memberships membership_content_type = ContentType.objects.get(app_label="projects", model="membership") - tl_filter |= Q(project__is_private=True, data_content_type=membership_content_type) + tl_filter |= Q(project__is_private=True, + project__anon_permissions__contains=["view_project"], + data_content_type=membership_content_type) # Filtering private projects where user is member if not user.is_anonymous(): @@ -132,6 +134,7 @@ def filter_timeline_for_user(timeline, user): for content_type_key, content_type in content_types.items(): if content_type_key in membership.role.permissions or membership.is_owner: tl_filter |= Q(project=membership.project, data_content_type=content_type) + tl_filter |= Q(project=membership.project, data_content_type=membership_content_type) timeline = timeline.filter(tl_filter) return timeline From ae34c6490fef693971ae45bb3318e9b381aabe6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 8 Aug 2015 21:34:48 +0200 Subject: [PATCH 092/190] [i18n] update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 40 +++-- taiga/locale/de/LC_MESSAGES/django.po | 42 +++-- taiga/locale/en/LC_MESSAGES/django.po | 36 ++-- taiga/locale/es/LC_MESSAGES/django.po | 40 +++-- taiga/locale/fi/LC_MESSAGES/django.po | 40 +++-- taiga/locale/fr/LC_MESSAGES/django.po | 42 +++-- taiga/locale/nl/LC_MESSAGES/django.po | 40 +++-- taiga/locale/pl/LC_MESSAGES/django.po | 42 +++-- taiga/locale/ru/LC_MESSAGES/django.po | 199 +++++++++++---------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 42 +++-- 10 files changed, 303 insertions(+), 260 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index acebafc2..309f6498 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,10 +9,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Catalan (http://www.transifex.com/projects/p/taiga-back/" +"Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -542,11 +542,11 @@ msgstr "Conté camps personalitzats invàlids." msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "" @@ -783,8 +783,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no és un arxiu json vàlid" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "El projecte no existeix" @@ -1368,23 +1368,23 @@ msgstr "nota de bloqueig" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tens permissos per a ficar aquest sprint a aquesta incidència" -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "No tens permissos per a ficar aquest status a aquesta tasca" -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "No tens permissos per a ficar aquesta severitat a aquesta tasca" -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "No tens permissos per a ficar aquesta prioritat a aquesta incidència" -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "No tens permissos per a ficar aquest tipus a aquesta incidència" @@ -2241,15 +2241,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2627,15 +2627,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2718,6 +2718,10 @@ msgstr "últim a modificar" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Informació personal" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 48eadfb2..d0703e98 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -13,10 +13,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-21 08:47+0000\n" -"Last-Translator: Regina \n" -"Language-Team: German (http://www.transifex.com/projects/p/taiga-back/" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"Last-Translator: Taiga Dev Team \n" +"Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -591,11 +591,11 @@ msgstr "Enthält ungültige Benutzerfelder." msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Fehler beim Erzeugen der Projekt Export-Datei " -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Fehler beim Laden von Projekt Export-Datei" @@ -925,8 +925,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Die Nutzlast ist kein gültiges json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Das Projekt existiert nicht" @@ -1534,27 +1534,27 @@ msgstr "Blockierungsgrund" msgid "sprint" msgstr "Sprint" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Sprint zu setzen." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Status zu setzen. " -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Gewichtung zu setzen." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Priorität zu setzen. " -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "Sie haben nicht die Berechtigung, das Ticket auf diese Art zu setzen." @@ -2691,18 +2691,18 @@ msgstr "Zukünftiger Sprint" msgid "Project End" msgstr "Projektende" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" "Sie haben nicht die Berechtigung, diesen Sprint auf diese Aufgabe zu setzen" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" "Sie haben nicht die Berechtigung, diese User-Story auf diese Aufgabe zu " "setzen" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -3097,15 +3097,15 @@ msgstr "Projekteigentümer " msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3188,6 +3188,10 @@ msgstr "letzte Änderung" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Personal Information" diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index b05f8874..cd3cc7fb 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -531,11 +531,11 @@ msgstr "" msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "" @@ -756,8 +756,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "" @@ -1341,23 +1341,23 @@ msgstr "" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "" -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "" -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "" -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "" -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "" @@ -2208,15 +2208,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2576,15 +2576,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2665,6 +2665,10 @@ msgstr "" msgid "href" msgstr "" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index ce90b2a1..78e4a34e 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,10 +12,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-06 14:16+0000\n" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:33+0000\n" "Last-Translator: David Barragán \n" -"Language-Team: Spanish (http://www.transifex.com/projects/p/taiga-back/" +"Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -580,11 +580,11 @@ msgstr "Contiene attributos personalizados inválidos." msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Erro generando el volcado de datos del proyecto" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Error cargando el volcado de datos del proyecto" @@ -908,8 +908,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no es un json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "El proyecto no existe" @@ -1534,23 +1534,23 @@ msgstr "nota de bloqueo" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tienes permisos para asignar un sprint a esta petición." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "No tienes permisos para asignar un estado a esta petición." -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "No tienes permisos para establecer la gravedad de esta petición." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "No tienes permiso para establecer la prioridad de esta petición." -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "No tienes permiso para establecer el tipo de esta petición." @@ -2641,15 +2641,15 @@ msgstr "Sprint futuro" msgid "Project End" msgstr "Final de proyecto" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "No tienes permisos para asignar este sprint a esta tarea." -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "No tienes permisos para asignar esta historia a esta tarea." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "No tienes permisos para asignar este estado a esta tarea." @@ -3061,17 +3061,17 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "No tienes permisos para asignar este sprint a esta historia de usuario." -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" "No tienes permisos para asignar este estado a esta historia de usuario." -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3154,6 +3154,10 @@ msgstr "última modificación por" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "Comprueba la API de histórico para obtener el diff exacto" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Información personal" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index be5f7ffb..3e28153d 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,10 +9,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Finnish (http://www.transifex.com/projects/p/taiga-back/" +"Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -567,11 +567,11 @@ msgstr "Sisältää vieheellisiä omia kenttiä." msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Virhe tiedoston luonnissa" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Virhe tiedoston latauksessa" @@ -894,8 +894,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "The payload is not a valid json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Projektia ei löydy" @@ -1497,23 +1497,23 @@ msgstr "suljettu muistiinpano" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "Sinulla ei ole oikeuksia laittaa kierrosta tälle pyynnölle." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "Sinulla ei ole oikeutta asettaa statusta tälle pyyntö." -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "Sinulla ei ole oikeutta asettaa vakavuutta tälle pyynnölle." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "Sinulla ei ole oikeutta asettaa kiireellisyyttä tälle pyynnölle." -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "Sinulla ei ole oikeutta asettaa tyyppiä tälle pyyntö." @@ -2610,15 +2610,15 @@ msgstr "Tuleva kierros" msgid "Project End" msgstr "Projektin loppu" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -3027,15 +3027,15 @@ msgstr "Tuoteomistaja" msgid "Stakeholder" msgstr "Sidosryhmä" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3118,6 +3118,10 @@ msgstr "viimeksi muokannut" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Henkilökohtaiset tiedot" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 82f0e823..96676d44 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -16,10 +16,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-21 06:58+0000\n" -"Last-Translator: Djyp Forest Fortin \n" -"Language-Team: French (http://www.transifex.com/projects/p/taiga-back/" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"Last-Translator: Taiga Dev Team \n" +"Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -596,11 +596,11 @@ msgstr "Contient des champs personnalisés non valides." msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Error dans la génération du dump du projet" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Erreur au chargement du dump du projet" @@ -880,8 +880,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Le payload n'est pas un json valide" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Le projet n'existe pas" @@ -1468,23 +1468,23 @@ msgstr "note bloquée" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "Vous n'avez pas la permission d'affecter ce sprint à ce problème." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "Vous n'avez pas la permission d'affecter ce statut à ce problème." -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "Vous n'avez pas la permission d'affecter cette sévérité à ce problème." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "Vous n'avez pas la permission d'affecter cette priorité à ce problème." -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "Vous n'avez pas la permission d'affecter ce type à ce problème." @@ -2354,15 +2354,15 @@ msgstr "Sprint futurs" msgid "Project End" msgstr "Fin du projet" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2759,15 +2759,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Participant" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2850,6 +2850,10 @@ msgstr "dernier modificateur" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Informations personnelles" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 2f7b9cd0..ddd91b72 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,10 +9,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-06 14:06+0000\n" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" -"Language-Team: Dutch (http://www.transifex.com/projects/p/taiga-back/" +"Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -580,11 +580,11 @@ msgstr "Het bevat ongeldige eigen velden:" msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Fout bij genereren project dump" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Fout bij laden project dump" @@ -843,8 +843,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "De payload is geen geldige json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Het project bestaat niet" @@ -1431,25 +1431,25 @@ msgstr "geblokkeerde notitie" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "Je hebt geen toestemming om deze sprint op deze issue te zetten." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "Je hebt geen toestemming om deze status toe te kennen aan dze issue." -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Je hebt geen toestemming om dit ernstniveau toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Je hebt geen toestemming om deze prioriteit toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "Je hebt geen toestemming om dit type toe te kennen aan deze issue." @@ -2330,15 +2330,15 @@ msgstr "Toekomstige sprint" msgid "Project End" msgstr "Project einde" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2722,15 +2722,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2813,6 +2813,10 @@ msgstr "gebruiker met laatste wijziging" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Persoonlijke info" diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 1fedd7a1..8cc51124 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -8,10 +8,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-07-26 13:15+0000\n" -"Last-Translator: Konrad Krawczuk \n" -"Language-Team: Polish (http://www.transifex.com/projects/p/taiga-back/" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"Last-Translator: Taiga Dev Team \n" +"Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -579,11 +579,11 @@ msgstr "Zawiera niewłaściwe pola niestandardowe." msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Błąd w trakcie generowania zrzutu projektu" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Błąd w trakcie wczytywania zrzutu projektu" @@ -909,8 +909,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Źródło nie jest prawidłowym plikiem json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Projekt nie istnieje" @@ -1537,23 +1537,23 @@ msgstr "zaglokowana notatka" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "Nie masz uprawnień do połączenia tego zgłoszenia ze sprintem." -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "Nie masz uprawnień do ustawienia rygoru dla tego zgłoszenia." -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "Nie masz uprawnień do ustawienia priorytetu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "Nie masz uprawnień do ustawienia typu dla tego zgłoszenia." @@ -2665,16 +2665,16 @@ msgstr "Przyszły sprint" msgid "Project End" msgstr "Zakończenie projektu" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "Nie masz uprawnień do ustawiania sprintu dla tego zadania." -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" "Nie masz uprawnień do ustawiania historyjki użytkownika dla tego zadania" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "Nie masz uprawnień do ustawiania statusu dla tego zadania" @@ -3085,17 +3085,17 @@ msgstr "Właściciel produktu" msgid "Stakeholder" msgstr "Interesariusz" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Nie masz uprawnień do ustawiania sprintu dla tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" "Nie masz uprawnień do ustawiania statusu do tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3178,6 +3178,10 @@ msgstr "ostatnio zmodyfikowane przez" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "Informacje osobiste" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 99cc19e8..00953b8a 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -3,16 +3,17 @@ # This file is distributed under the same license as the taiga-back package. # # Translators: +# Dmitry Lobanov , 2015 # Dmitry Vinokurov , 2015 # Марат , 2015 msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-08-01 02:52+0000\n" -"Last-Translator: Марат \n" -"Language-Team: Russian (http://www.transifex.com/projects/p/taiga-back/" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"Last-Translator: Taiga Dev Team \n" +"Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -585,11 +586,11 @@ msgstr "Содержит неверные специальные поля" msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "Ошибка создания свалочного файла для проекта" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "Ошибка загрузки свалочного файла проекта" @@ -913,8 +914,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Нагрузочный файл не является правильным json-файлом" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "Проект не существует" @@ -1542,27 +1543,27 @@ msgstr "Заметка о блокировке" msgid "sprint" msgstr "спринт" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "У вас нет прав для того чтобы установить такой спринт для этой проблемы" -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "" "У вас нет прав для того чтобы установить такой статус для этой проблемы" -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "" "У вас нет прав для того чтобы установить такую важность для этой проблемы" -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "" "У вас нет прав для того чтобы установить такой приоритет для этой проблемы" -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "У вас нет прав для того чтобы установить такой тип для этой проблемы" @@ -2662,16 +2663,16 @@ msgstr "Будущий спринт" msgid "Project End" msgstr "Окончание проекта" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "У вас нет прав, чтобы назначить этот спринт для этой задачи." -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "" "У вас нет прав, чтобы назначить эту историю от пользователя этой задаче." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "У вас нет прав, чтобы установить этот статус для этой задачи." @@ -2845,7 +2846,7 @@ msgstr "" #. Translators: Name of scrum project template. #: taiga/projects/translations.py:28 msgid "Scrum" -msgstr "" +msgstr "Scrum" #. Translators: Description of scrum project template. #: taiga/projects/translations.py:30 @@ -2861,7 +2862,7 @@ msgstr "" #. Translators: Name of kanban project template. #: taiga/projects/translations.py:33 msgid "Kanban" -msgstr "" +msgstr "Kanban" #. Translators: Description of kanban project template. #: taiga/projects/translations.py:35 @@ -2875,62 +2876,62 @@ msgstr "" #. Translators: User story point value (value = undefined) #: taiga/projects/translations.py:43 msgid "?" -msgstr "" +msgstr "?" #. Translators: User story point value (value = 0) #: taiga/projects/translations.py:45 msgid "0" -msgstr "" +msgstr "0" #. Translators: User story point value (value = 0.5) #: taiga/projects/translations.py:47 msgid "1/2" -msgstr "" +msgstr "1/2" #. Translators: User story point value (value = 1) #: taiga/projects/translations.py:49 msgid "1" -msgstr "" +msgstr "1" #. Translators: User story point value (value = 2) #: taiga/projects/translations.py:51 msgid "2" -msgstr "" +msgstr "2" #. Translators: User story point value (value = 3) #: taiga/projects/translations.py:53 msgid "3" -msgstr "" +msgstr "3" #. Translators: User story point value (value = 5) #: taiga/projects/translations.py:55 msgid "5" -msgstr "" +msgstr "5" #. Translators: User story point value (value = 8) #: taiga/projects/translations.py:57 msgid "8" -msgstr "" +msgstr "8" #. Translators: User story point value (value = 10) #: taiga/projects/translations.py:59 msgid "10" -msgstr "" +msgstr "10" #. Translators: User story point value (value = 13) #: taiga/projects/translations.py:61 msgid "13" -msgstr "" +msgstr "13" #. Translators: User story point value (value = 20) #: taiga/projects/translations.py:63 msgid "20" -msgstr "" +msgstr "20" #. Translators: User story point value (value = 40) #: taiga/projects/translations.py:65 msgid "40" -msgstr "" +msgstr "40" #. Translators: User story status #. Translators: Task status @@ -2938,12 +2939,12 @@ msgstr "" #: taiga/projects/translations.py:73 taiga/projects/translations.py:96 #: taiga/projects/translations.py:112 msgid "New" -msgstr "" +msgstr "Новая" #. Translators: User story status #: taiga/projects/translations.py:76 msgid "Ready" -msgstr "" +msgstr "Готово" #. Translators: User story status #. Translators: Task status @@ -2951,7 +2952,7 @@ msgstr "" #: taiga/projects/translations.py:79 taiga/projects/translations.py:98 #: taiga/projects/translations.py:114 msgid "In progress" -msgstr "" +msgstr "В процессе" #. Translators: User story status #. Translators: Task status @@ -2959,29 +2960,29 @@ msgstr "" #: taiga/projects/translations.py:82 taiga/projects/translations.py:100 #: taiga/projects/translations.py:116 msgid "Ready for test" -msgstr "" +msgstr "Можно проверять" #. Translators: User story status #: taiga/projects/translations.py:85 msgid "Done" -msgstr "" +msgstr "Завершена" #. Translators: User story status #: taiga/projects/translations.py:88 msgid "Archived" -msgstr "" +msgstr "Архивирована" #. Translators: Task status #. Translators: Issue status #: taiga/projects/translations.py:102 taiga/projects/translations.py:118 msgid "Closed" -msgstr "" +msgstr "Закрыта" #. Translators: Task status #. Translators: Issue status #: taiga/projects/translations.py:104 taiga/projects/translations.py:120 msgid "Needs Info" -msgstr "" +msgstr "Требуются подробности" #. Translators: Issue status #: taiga/projects/translations.py:122 @@ -2991,58 +2992,58 @@ msgstr "" #. Translators: Issue status #: taiga/projects/translations.py:124 msgid "Rejected" -msgstr "" +msgstr "Отклонена" #. Translators: Issue type #: taiga/projects/translations.py:132 msgid "Bug" -msgstr "" +msgstr "Ошибка" #. Translators: Issue type #: taiga/projects/translations.py:134 msgid "Question" -msgstr "" +msgstr "Вопрос" #. Translators: Issue type #: taiga/projects/translations.py:136 msgid "Enhancement" -msgstr "" +msgstr "Улучшение" #. Translators: Issue priority #: taiga/projects/translations.py:144 msgid "Low" -msgstr "" +msgstr "Низкий" #. Translators: Issue priority #. Translators: Issue severity #: taiga/projects/translations.py:146 taiga/projects/translations.py:159 msgid "Normal" -msgstr "" +msgstr "Обычный" #. Translators: Issue priority #: taiga/projects/translations.py:148 msgid "High" -msgstr "" +msgstr "Высокий" #. Translators: Issue severity #: taiga/projects/translations.py:155 msgid "Wishlist" -msgstr "" +msgstr "Список пожеланий" #. Translators: Issue severity #: taiga/projects/translations.py:157 msgid "Minor" -msgstr "" +msgstr "Низкий" #. Translators: Issue severity #: taiga/projects/translations.py:161 msgid "Important" -msgstr "" +msgstr "Важный" #. Translators: Issue severity #: taiga/projects/translations.py:163 msgid "Critical" -msgstr "" +msgstr "Критический" #. Translators: User role #: taiga/projects/translations.py:170 @@ -3052,37 +3053,39 @@ msgstr "" #. Translators: User role #: taiga/projects/translations.py:172 msgid "Design" -msgstr "" +msgstr "Дизайнер" #. Translators: User role #: taiga/projects/translations.py:174 msgid "Front" -msgstr "" +msgstr "Фронтенд разработчик" #. Translators: User role #: taiga/projects/translations.py:176 msgid "Back" -msgstr "" +msgstr "Бэкенд разработчик" #. Translators: User role #: taiga/projects/translations.py:178 msgid "Product Owner" -msgstr "" +msgstr "Владелец продукта" #. Translators: User role #: taiga/projects/translations.py:180 msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" +"У вас нет прав чтобы установить спринт для этой пользовательской истории." -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" +"У вас нет прав чтобы установить статус для этой пользовательской истории." -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3091,28 +3094,28 @@ msgstr "" #: taiga/projects/userstories/models.py:37 msgid "role" -msgstr "" +msgstr "роль" #: taiga/projects/userstories/models.py:75 msgid "backlog order" -msgstr "" +msgstr "порядок списка задач" #: taiga/projects/userstories/models.py:77 #: taiga/projects/userstories/models.py:79 msgid "sprint order" -msgstr "" +msgstr "порядок спринтов" #: taiga/projects/userstories/models.py:87 msgid "finish date" -msgstr "" +msgstr "дата окончания" #: taiga/projects/userstories/models.py:95 msgid "is client requirement" -msgstr "" +msgstr "является требованием клиента" #: taiga/projects/userstories/models.py:97 msgid "is team requirement" -msgstr "" +msgstr "является требованием команды" #: taiga/projects/userstories/models.py:102 msgid "generated from issue" @@ -3120,40 +3123,40 @@ msgstr "" #: taiga/projects/userstories/validators.py:28 msgid "There's no user story with that id" -msgstr "" +msgstr "Не существует пользовательской истории с таким идентификатором" #: taiga/projects/validators.py:28 msgid "There's no project with that id" -msgstr "" +msgstr "Не существует проекта с таким идентификатором" #: taiga/projects/validators.py:37 msgid "There's no user story status with that id" -msgstr "" +msgstr "Не существует статуса пользовательской истории с таким идентификатором" #: taiga/projects/validators.py:46 msgid "There's no task status with that id" -msgstr "" +msgstr "Не существует статуса задачи с таким идентификатором" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:54 msgid "Votes" -msgstr "" +msgstr "Голоса" #: taiga/projects/votes/models.py:50 msgid "votes" -msgstr "" +msgstr "голоса" #: taiga/projects/votes/models.py:53 msgid "Vote" -msgstr "" +msgstr "Голосовать" #: taiga/projects/wiki/api.py:60 msgid "'content' parameter is mandatory" -msgstr "" +msgstr "параметр 'content' является обязательным" #: taiga/projects/wiki/api.py:63 msgid "'project_id' parameter is mandatory" -msgstr "" +msgstr "параметр 'project_id' является обязательным" #: taiga/projects/wiki/models.py:36 msgid "last modifier" @@ -3161,15 +3164,19 @@ msgstr "" #: taiga/projects/wiki/models.py:69 msgid "href" +msgstr "href" + +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" msgstr "" #: taiga/users/admin.py:50 msgid "Personal info" -msgstr "" +msgstr "Личные данные" #: taiga/users/admin.py:52 msgid "Permissions" -msgstr "" +msgstr "Права доступа" #: taiga/users/admin.py:53 msgid "Important dates" @@ -3177,11 +3184,11 @@ msgstr "" #: taiga/users/api.py:124 taiga/users/api.py:131 msgid "Invalid username or email" -msgstr "" +msgstr "Неверное имя пользователя или e-mail" #: taiga/users/api.py:140 msgid "Mail sended successful!" -msgstr "" +msgstr "Письмо успешно отправлено!" #: taiga/users/api.py:152 taiga/users/api.py:157 msgid "Token is invalid" @@ -3197,11 +3204,11 @@ msgstr "" #: taiga/users/api.py:184 msgid "Invalid password length at least 6 charaters needed" -msgstr "" +msgstr "Неверная длина пароля, требуется как минимум 6 символов" #: taiga/users/api.py:187 msgid "Invalid current password" -msgstr "" +msgstr "Неверно указан текущий пароль" #: taiga/users/api.py:203 msgid "Incomplete arguments" @@ -3209,11 +3216,11 @@ msgstr "" #: taiga/users/api.py:208 msgid "Invalid image format" -msgstr "" +msgstr "Неправильный формат изображения" #: taiga/users/api.py:261 msgid "Duplicated email" -msgstr "" +msgstr "Этот email уже используется" #: taiga/users/api.py:263 msgid "Not valid email" @@ -3230,7 +3237,7 @@ msgstr "" #: taiga/users/models.py:69 msgid "superuser status" -msgstr "" +msgstr "статус суперпользователя" #: taiga/users/models.py:70 msgid "" @@ -3240,16 +3247,16 @@ msgstr "" #: taiga/users/models.py:100 msgid "username" -msgstr "" +msgstr "имя пользователя" #: taiga/users/models.py:101 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" -msgstr "" +msgstr "Обязательно. 30 символов или меньше. Буквы, числа и символы /./-/_" #: taiga/users/models.py:104 msgid "Enter a valid username." -msgstr "" +msgstr "Введите корректное имя пользователя." #: taiga/users/models.py:107 msgid "active" @@ -3263,11 +3270,11 @@ msgstr "" #: taiga/users/models.py:114 msgid "biography" -msgstr "" +msgstr "биография" #: taiga/users/models.py:117 msgid "photo" -msgstr "" +msgstr "фотография" #: taiga/users/models.py:118 msgid "date joined" @@ -3275,15 +3282,15 @@ msgstr "" #: taiga/users/models.py:120 msgid "default language" -msgstr "" +msgstr "язык по умолчанию" #: taiga/users/models.py:122 msgid "default theme" -msgstr "" +msgstr "тема по умолчанию" #: taiga/users/models.py:124 msgid "default timezone" -msgstr "" +msgstr "временная зона по умолчанию" #: taiga/users/models.py:126 msgid "colorize tags" @@ -3295,7 +3302,7 @@ msgstr "" #: taiga/users/models.py:133 msgid "new email address" -msgstr "" +msgstr "новый email адрес" #: taiga/users/models.py:188 msgid "permissions" @@ -3307,11 +3314,11 @@ msgstr "" #: taiga/users/serializers.py:70 msgid "Invalid username. Try with a different one." -msgstr "" +msgstr "Неверное имя пользователя. Попробуйте другое." #: taiga/users/services.py:48 taiga/users/services.py:52 msgid "Username or password does not matches user." -msgstr "" +msgstr "Имя пользователя или пароль не соответствуют пользователю." #: taiga/users/templates/emails/change_email-body-html.jinja:4 #, python-format @@ -3342,7 +3349,7 @@ msgstr "" #: taiga/users/templates/emails/change_email-subject.jinja:1 msgid "[Taiga] Change email" -msgstr "" +msgstr "[Taiga] Изменить e-mail" #: taiga/users/templates/emails/password_recovery-body-html.jinja:4 #, python-format @@ -3373,7 +3380,7 @@ msgstr "" #: taiga/users/templates/emails/password_recovery-subject.jinja:1 msgid "[Taiga] Password recovery" -msgstr "" +msgstr "[Taiga] Восстановление пароля" #: taiga/users/templates/emails/registered_user-body-html.jinja:6 msgid "" @@ -3432,7 +3439,7 @@ msgstr "" #: taiga/users/validators.py:29 msgid "There's no role with that id" -msgstr "" +msgstr "Не существует роли с таким идентификатором" #: taiga/userstorage/api.py:50 msgid "" @@ -3445,11 +3452,11 @@ msgstr "" #: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 msgid "URL" -msgstr "" +msgstr "URL" #: taiga/webhooks/models.py:29 msgid "secret key" -msgstr "" +msgstr "Секретный ключ" #: taiga/webhooks/models.py:39 msgid "status code" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 2223881c..bc7e7cc4 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,10 +11,10 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-03 11:09+0200\n" -"PO-Revision-Date: 2015-08-01 03:51+0000\n" -"Last-Translator: Chi-Hsun Tsai \n" -"Language-Team: Chinese Traditional (http://www.transifex.com/projects/p/" +"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"Last-Translator: Taiga Dev Team \n" +"Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -572,11 +572,11 @@ msgstr "包括無效慣例欄位" msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" -#: taiga/export_import/tasks.py:49 taiga/export_import/tasks.py:50 +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 msgid "Error generating project dump" msgstr "產生專案傾倒時出錯" -#: taiga/export_import/tasks.py:82 taiga/export_import/tasks.py:83 +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 msgid "Error loading project dump" msgstr "載入專案傾倒時出錯" @@ -895,8 +895,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "載荷為無效json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:134 -#: taiga/projects/tasks/api.py:78 taiga/projects/userstories/api.py:103 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 msgid "The project doesn't exist" msgstr "專案不存在" @@ -1519,23 +1519,23 @@ msgstr "封鎖筆記" msgid "sprint" msgstr "衝刺任務" -#: taiga/projects/issues/api.py:154 +#: taiga/projects/issues/api.py:157 msgid "You don't have permissions to set this sprint to this issue." msgstr "您無權限設定此問題的衝刺任務" -#: taiga/projects/issues/api.py:158 +#: taiga/projects/issues/api.py:161 msgid "You don't have permissions to set this status to this issue." msgstr "您無權限設定此問題的狀態" -#: taiga/projects/issues/api.py:162 +#: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." msgstr "您無權限設定此問題的嚴重性" -#: taiga/projects/issues/api.py:166 +#: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." msgstr "您無權限設定此問題的優先性" -#: taiga/projects/issues/api.py:170 +#: taiga/projects/issues/api.py:173 msgid "You don't have permissions to set this type to this issue." msgstr "您無權限設定此問題的類型" @@ -2636,15 +2636,15 @@ msgstr "未來之衝刺" msgid "Project End" msgstr "專案結束" -#: taiga/projects/tasks/api.py:94 taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 msgid "You don't have permissions to set this sprint to this task." msgstr "無權限更動此任務下的衝刺任務" -#: taiga/projects/tasks/api.py:97 +#: taiga/projects/tasks/api.py:100 msgid "You don't have permissions to set this user story to this task." msgstr "無權限更動此務下的使用者故事" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "無權限更動此任務下的狀態" @@ -3046,15 +3046,15 @@ msgstr "產品所有人" msgid "Stakeholder" msgstr "利害關係人" -#: taiga/projects/userstories/api.py:150 +#: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "無權限更動使用者故事的衝刺任務" -#: taiga/projects/userstories/api.py:154 +#: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "無權限更動此使用者故事的狀態" -#: taiga/projects/userstories/api.py:248 +#: taiga/projects/userstories/api.py:251 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3136,6 +3136,10 @@ msgstr "上次更改" msgid "href" msgstr "href" +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + #: taiga/users/admin.py:50 msgid "Personal info" msgstr "個人資訊" From 95c57eb7e5bc9e577ea4d7ac6186a61c6c4722e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 13 Aug 2015 10:04:33 +0200 Subject: [PATCH 093/190] [i18n] Update locales --- taiga/locale/de/LC_MESSAGES/django.po | 10 ++++++++-- taiga/locale/pl/LC_MESSAGES/django.po | 14 +++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index d0703e98..65ab24a3 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -4,6 +4,7 @@ # # Translators: # Chris , 2015 +# Christoph Harrer, 2015 # Hans Raaf, 2015 # Hans Raaf, 2015 # Regina , 2015 @@ -14,8 +15,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-08 21:28+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-08-12 11:36+0000\n" +"Last-Translator: Christoph Harrer\n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -2705,6 +2706,7 @@ msgstr "" #: taiga/projects/tasks/api.py:103 msgid "You don't have permissions to set this status to this task." msgstr "" +"Sie haben nicht die Berechtigung, diesen Status auf diese Aufgabe zu setzen." #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -3100,10 +3102,14 @@ msgstr "Stakeholder" #: taiga/projects/userstories/api.py:153 msgid "You don't have permissions to set this sprint to this user story." msgstr "" +"Sie haben nicht die Berechtigung, diesen Sprint auf diese User-Story zu " +"setzen." #: taiga/projects/userstories/api.py:157 msgid "You don't have permissions to set this status to this user story." msgstr "" +"Sie haben nicht die Berechtigung, diesen Status auf diese User-Story zu " +"setzen." #: taiga/projects/userstories/api.py:251 #, python-brace-format diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 8cc51124..4495f598 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-08 21:28+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-08-12 18:09+0000\n" +"Last-Translator: Konrad Krawczuk \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -1547,7 +1547,7 @@ msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." #: taiga/projects/issues/api.py:165 msgid "You don't have permissions to set this severity to this issue." -msgstr "Nie masz uprawnień do ustawienia rygoru dla tego zgłoszenia." +msgstr "Nie masz uprawnień do ustawienia ważności dla tego zgłoszenia." #: taiga/projects/issues/api.py:169 msgid "You don't have permissions to set this priority to this issue." @@ -1569,7 +1569,7 @@ msgstr "status" #: taiga/projects/issues/models.py:42 msgid "severity" -msgstr "rygor" +msgstr "ważność" #: taiga/projects/issues/models.py:44 msgid "priority" @@ -1686,7 +1686,7 @@ msgstr "domyślny priorytet" #: taiga/projects/models.py:107 msgid "default severity" -msgstr "domyślny rygor" +msgstr "domyślna ważność" #: taiga/projects/models.py:111 msgid "default issue status" @@ -1810,7 +1810,7 @@ msgstr "priorytety" #: taiga/projects/models.py:590 msgid "severities" -msgstr "rygory" +msgstr "ważność" #: taiga/projects/models.py:591 msgid "roles" @@ -2651,7 +2651,7 @@ msgstr "Priorytety" #: taiga/projects/serializers.py:412 msgid "Severities" -msgstr "Rygory" +msgstr "Ważność" #: taiga/projects/serializers.py:413 msgid "Roles" From 811a6c79064dd74231f866d212cae614a48c2171 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 14 Aug 2015 10:08:21 +0200 Subject: [PATCH 094/190] Issue - #3185 Import user story with role points results in an IntegrityError --- taiga/export_import/service.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index 1ca76eb0..40578118 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -26,6 +26,7 @@ from taiga.projects.history.services import make_key_from_model_object, take_sna from taiga.timeline.service import build_project_namespace from taiga.projects.references import sequences as seq from taiga.projects.references import models as refs +from taiga.projects.userstories.models import RolePoints from taiga.projects.services import find_invited_user from . import serializers @@ -351,9 +352,17 @@ def store_wiki_link(project, wiki_link): def store_role_point(project, us, role_point): serialized = serializers.RolePointsExportSerializer(data=role_point, context={"project": project}) if serialized.is_valid(): - serialized.object.user_story = us - serialized.save() - return serialized.object + try: + existing_role_point = us.role_points.get(role=serialized.object.role) + existing_role_point.points = serialized.object.points + existing_role_point.save() + return existing_role_point + + except RolePoints.DoesNotExist: + serialized.object.user_story = us + serialized.save() + return serialized.object + add_errors("role_points", serialized.errors) return None From 5dbdedd44d1a8ed6c5457268afea9b6c176945e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 24 Aug 2015 10:36:49 +0200 Subject: [PATCH 095/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/de/LC_MESSAGES/django.po | 27 +++++++++++++--------- taiga/locale/en/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/es/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/fi/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/fr/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/nl/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/pl/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/ru/LC_MESSAGES/django.po | 16 ++++++------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 16 ++++++------- 10 files changed, 88 insertions(+), 83 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 309f6498..731e1b2b 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -536,9 +536,9 @@ msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "" @@ -792,13 +792,13 @@ msgstr "El projecte no existeix" msgid "Bad signature" msgstr "Firma no vàlida." -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'element referenciat no existeix" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'estatus no existeix." diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 65ab24a3..767924bb 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -7,6 +7,7 @@ # Christoph Harrer, 2015 # Hans Raaf, 2015 # Hans Raaf, 2015 +# Henning Matthaei, 2015 # Regina , 2015 # Sebastian Blum , 2015 # Thomas McWork , 2015 @@ -14,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" -"PO-Revision-Date: 2015-08-12 11:36+0000\n" -"Last-Translator: Christoph Harrer\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" +"PO-Revision-Date: 2015-08-16 08:52+0000\n" +"Last-Translator: Henning Matthaei\n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -197,6 +198,8 @@ msgstr "" #: taiga/base/api/fields.py:957 msgid "Please either submit a file or check the clear checkbox, not both." msgstr "" +"Bitte senden Sie entweder eine Datei oder markieren Sie \"Löschen\", nicht " +"beides." #: taiga/base/api/fields.py:997 msgid "" @@ -586,9 +589,9 @@ msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" @@ -935,13 +938,13 @@ msgstr "Das Projekt existiert nicht" msgid "Bad signature" msgstr "Falsche Signatur" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Das referenzierte Element existiert nicht" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Der Status existiert nicht" @@ -1364,7 +1367,7 @@ msgstr "Text" #: taiga/projects/custom_attributes/models.py:34 msgid "Multi-Line Text" -msgstr "" +msgstr "Mehrzeiliger Text" #: taiga/projects/custom_attributes/models.py:36 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 @@ -3514,6 +3517,8 @@ msgid "" "\n" "You may remove your account from this service: %(url)s\n" msgstr "" +"\n" +"Sie können Ihren Account von diesem Dienst trennen: %(url)s\n" #: taiga/users/templates/emails/registered_user-subject.jinja:1 msgid "You've been Taigatized!" diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index cd3cc7fb..4901c6a8 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -525,9 +525,9 @@ msgid "It contain invalid custom fields." msgstr "" #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "" @@ -765,13 +765,13 @@ msgstr "" msgid "Bad signature" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 78e4a34e..e0da36cc 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:33+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -574,9 +574,9 @@ msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" @@ -917,13 +917,13 @@ msgstr "El proyecto no existe" msgid "Bad signature" msgstr "Firma errónea" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "El elemento referenciado no existe" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "El estado no existe" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 3e28153d..cd886a04 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -561,9 +561,9 @@ msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" @@ -903,13 +903,13 @@ msgstr "Projektia ei löydy" msgid "Bad signature" msgstr "Virheellinen allekirjoitus" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Viitattu elementtiä ei löydy" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Tilaa ei löydy" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 96676d44..d7deb7f8 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -16,7 +16,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -590,9 +590,9 @@ msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" @@ -889,13 +889,13 @@ msgstr "Le projet n'existe pas" msgid "Bad signature" msgstr "Signature non valide" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'élément référencé n'existe pas" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'état n'existe pas" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index ddd91b72..a8265e1c 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -574,9 +574,9 @@ msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" @@ -852,13 +852,13 @@ msgstr "Het project bestaat niet" msgid "Bad signature" msgstr "Slechte signature" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Het element waarnaar verwezen wordt bestaat niet" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "De status bestaat niet" diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 4495f598..80d995d8 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-12 18:09+0000\n" "Last-Translator: Konrad Krawczuk \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -573,9 +573,9 @@ msgid "It contain invalid custom fields." msgstr "Zawiera niewłaściwe pola niestandardowe." #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" @@ -918,13 +918,13 @@ msgstr "Projekt nie istnieje" msgid "Bad signature" msgstr "Błędna sygnatura" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Element referencyjny nie istnieje" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Status nie istnieje" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 00953b8a..7855a123 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" @@ -580,9 +580,9 @@ msgid "It contain invalid custom fields." msgstr "Содержит неверные специальные поля" #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" @@ -923,13 +923,13 @@ msgstr "Проект не существует" msgid "Bad signature" msgstr "Плохая подпись" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Указанный элемент не существует" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Статус не существует" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index bc7e7cc4..87d46ba4 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-08 21:28+0200\n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" "PO-Revision-Date: 2015-08-08 19:29+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" @@ -566,9 +566,9 @@ msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" #: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 -#: taiga/projects/serializers.py:67 taiga/projects/serializers.py:93 -#: taiga/projects/serializers.py:124 taiga/projects/serializers.py:167 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" @@ -904,13 +904,13 @@ msgstr "專案不存在" msgid "Bad signature" msgstr "錯誤簽名" -#: taiga/hooks/bitbucket/event_hooks.py:73 -#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:73 +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "參考元素不存在" -#: taiga/hooks/bitbucket/event_hooks.py:80 -#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:80 +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "狀態不存在" From d34f47ccff8c3d1a74fa0b6bdcab6c61dfed1b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 4 Aug 2015 15:01:53 +0200 Subject: [PATCH 096/190] Add has-closed-milestones to the response headers when upsdate us orders in bulk for sprints --- taiga/projects/userstories/api.py | 6 ++++ tests/integration/test_userstories.py | 45 +++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 4f065547..b364a53c 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -226,6 +226,12 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi field=order_field) services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user) + if order_field == "sprint_order": + # NOTE: This is useful according to issue #2851 to update sprints column in the + # browser client when move USs from the backlog to an sprint or between sprints. + has_closed_milestones = project.milestones.filter(closed=True).exists() + self.headers["Taiga-Info-Has-Closed-Milestones"] = has_closed_milestones + return response.NoContent() @list_route(methods=["POST"]) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index c072b76c..d8972da2 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -7,6 +7,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects.userstories import services, models +from taiga.projects.userstories.serializers import UserStorySerializer from .. import factories as f @@ -107,7 +108,7 @@ def test_api_create_in_bulk_with_status(client): assert response.data[0]["status"] == project.default_us_status.id -def test_api_update_backlog_order_in_bulk(client): +def test_api_update_orders_in_bulk(client): project = f.create_project() f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) us1 = f.create_userstory(project=project) @@ -133,8 +134,48 @@ def test_api_update_backlog_order_in_bulk(client): assert response2.status_code == 204, response2.data assert response3.status_code == 204, response3.data +def test_api_update_orders_in_bulk_to_test_extra_headers(client): + project = f.create_project() + f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) + us1 = f.create_userstory(project=project) + us2 = f.create_userstory(project=project) -from taiga.projects.userstories.serializers import UserStorySerializer + url1 = reverse("userstories-bulk-update-backlog-order") + url2 = reverse("userstories-bulk-update-kanban-order") + url3 = reverse("userstories-bulk-update-sprint-order") + + data = { + "project_id": project.id, + "bulk_stories": [{"us_id": us1.id, "order": 1}, + {"us_id": us2.id, "order": 2}] + } + + client.login(project.owner) + + response1 = client.json.post(url1, json.dumps(data)) + response2 = client.json.post(url2, json.dumps(data)) + response3 = client.json.post(url3, json.dumps(data)) + assert response1.status_code == 204 + assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response2.status_code == 204 + assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response3.status_code == 204 + assert response3.has_header("Taiga-Info-Has-Closed-Milestones") == True + assert response3["taiga-info-has-closed-milestones"] == "False" + + us1.milestone.closed = True + us1.milestone.save() + + response1 = client.json.post(url1, json.dumps(data)) + response2 = client.json.post(url2, json.dumps(data)) + response3 = client.json.post(url3, json.dumps(data)) + assert response1.status_code == 204 + assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response2.status_code == 204 + assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response3.status_code == 204 + assert response3.has_header("Taiga-Info-Has-Closed-Milestones") == True + assert response3["taiga-info-has-closed-milestones"] == "True" def test_update_userstory_points(client): From 132caab8d9d3dc5c2e39458ea56f777de62bfa18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 25 Aug 2015 11:21:51 +0200 Subject: [PATCH 097/190] Add partial_update_perms to permission classes to allow put requests --- taiga/projects/attachments/permissions.py | 4 ++++ taiga/projects/custom_attributes/permissions.py | 6 ++++++ taiga/projects/issues/permissions.py | 2 ++ taiga/projects/milestones/permissions.py | 1 + taiga/projects/notifications/permissions.py | 1 + taiga/projects/permissions.py | 9 +++++++++ taiga/projects/tasks/permissions.py | 1 + taiga/projects/userstories/permissions.py | 1 + taiga/users/permissions.py | 2 ++ taiga/webhooks/permissions.py | 1 + 10 files changed, 28 insertions(+) diff --git a/taiga/projects/attachments/permissions.py b/taiga/projects/attachments/permissions.py index 47d922aa..f709c378 100644 --- a/taiga/projects/attachments/permissions.py +++ b/taiga/projects/attachments/permissions.py @@ -30,6 +30,7 @@ class UserStoryAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_us') | IsAttachmentOwnerPerm() create_perms = HasProjectPerm('modify_us') update_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() + partial_update_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_us') | IsAttachmentOwnerPerm() list_perms = AllowAny() @@ -38,6 +39,7 @@ class TaskAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_tasks') | IsAttachmentOwnerPerm() create_perms = HasProjectPerm('modify_task') update_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() + partial_update_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_task') | IsAttachmentOwnerPerm() list_perms = AllowAny() @@ -46,6 +48,7 @@ class IssueAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_issues') | IsAttachmentOwnerPerm() create_perms = HasProjectPerm('modify_issue') update_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() + partial_update_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_issue') | IsAttachmentOwnerPerm() list_perms = AllowAny() @@ -54,6 +57,7 @@ class WikiAttachmentPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_wiki_pages') | IsAttachmentOwnerPerm() create_perms = HasProjectPerm('modify_wiki_page') update_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() + partial_update_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() destroy_perms = HasProjectPerm('modify_wiki_page') | IsAttachmentOwnerPerm() list_perms = AllowAny() diff --git a/taiga/projects/custom_attributes/permissions.py b/taiga/projects/custom_attributes/permissions.py index 160d340e..14307d1a 100644 --- a/taiga/projects/custom_attributes/permissions.py +++ b/taiga/projects/custom_attributes/permissions.py @@ -31,6 +31,7 @@ class UserStoryCustomAttributePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -42,6 +43,7 @@ class TaskCustomAttributePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -53,6 +55,7 @@ class IssueCustomAttributePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -67,6 +70,7 @@ class UserStoryCustomAttributesValuesPermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_us') update_perms = HasProjectPerm('modify_us') + partial_update_perms = HasProjectPerm('modify_us') class TaskCustomAttributesValuesPermission(TaigaResourcePermission): @@ -74,6 +78,7 @@ class TaskCustomAttributesValuesPermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_tasks') update_perms = HasProjectPerm('modify_task') + partial_update_perms = HasProjectPerm('modify_task') class IssueCustomAttributesValuesPermission(TaigaResourcePermission): @@ -81,3 +86,4 @@ class IssueCustomAttributesValuesPermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_issues') update_perms = HasProjectPerm('modify_issue') + partial_update_perms = HasProjectPerm('modify_issue') diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 0eecf121..076b57a0 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -26,6 +26,7 @@ class IssuePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_issues') create_perms = HasProjectPerm('add_issue') update_perms = HasProjectPerm('modify_issue') + partial_update_perms = HasProjectPerm('modify_issue') destroy_perms = HasProjectPerm('delete_issue') list_perms = AllowAny() filters_data_perms = AllowAny() @@ -50,5 +51,6 @@ class IssueVotersPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_issues') create_perms = HasProjectPerm('add_issue') update_perms = HasProjectPerm('modify_issue') + partial_update_perms = HasProjectPerm('modify_issue') destroy_perms = HasProjectPerm('delete_issue') list_perms = HasProjectPerm('view_issues') diff --git a/taiga/projects/milestones/permissions.py b/taiga/projects/milestones/permissions.py index c0a25230..9823c8de 100644 --- a/taiga/projects/milestones/permissions.py +++ b/taiga/projects/milestones/permissions.py @@ -25,6 +25,7 @@ class MilestonePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_milestones') create_perms = HasProjectPerm('add_milestone') update_perms = HasProjectPerm('modify_milestone') + partial_update_perms = HasProjectPerm('modify_milestone') destroy_perms = HasProjectPerm('delete_milestone') list_perms = AllowAny() stats_perms = HasProjectPerm('view_milestones') diff --git a/taiga/projects/notifications/permissions.py b/taiga/projects/notifications/permissions.py index 9b6e9d63..a89b9caf 100644 --- a/taiga/projects/notifications/permissions.py +++ b/taiga/projects/notifications/permissions.py @@ -21,5 +21,6 @@ class NotifyPolicyPermission(TaigaResourcePermission): retrieve_perms = IsAuthenticated() create_perms = IsAuthenticated() update_perms = IsAuthenticated() + partial_update_perms = IsAuthenticated() destroy_perms = IsAuthenticated() list_perms = IsAuthenticated() diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 943ef379..ff152e58 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -71,6 +71,7 @@ class MembershipPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_create_perms = IsProjectOwner() @@ -83,6 +84,7 @@ class PointsPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -92,6 +94,7 @@ class UserStoryStatusPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -103,6 +106,7 @@ class TaskStatusPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -114,6 +118,7 @@ class SeverityPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -123,6 +128,7 @@ class PriorityPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -132,6 +138,7 @@ class IssueStatusPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -141,6 +148,7 @@ class IssueTypePermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() bulk_update_order_perms = IsProjectOwner() @@ -152,5 +160,6 @@ class ProjectTemplatePermission(TaigaResourcePermission): retrieve_perms = AllowAny() create_perms = IsSuperUser() update_perms = IsSuperUser() + partial_update_perms = IsSuperUser() destroy_perms = IsSuperUser() list_perms = AllowAny() diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index f97215b8..2c1fd7b0 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -24,6 +24,7 @@ class TaskPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_tasks') create_perms = HasProjectPerm('add_task') update_perms = HasProjectPerm('modify_task') + partial_update_perms = HasProjectPerm('modify_task') destroy_perms = HasProjectPerm('delete_task') list_perms = AllowAny() csv_perms = AllowAny() diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 32c14e01..3aa548db 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -23,6 +23,7 @@ class UserStoryPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_us') create_perms = HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us') update_perms = HasProjectPerm('modify_us') + partial_update_perms = HasProjectPerm('modify_us') destroy_perms = HasProjectPerm('delete_us') list_perms = AllowAny() filters_data_perms = AllowAny() diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index 70cfa3b8..bad16f1a 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -34,6 +34,7 @@ class UserPermission(TaigaResourcePermission): retrieve_perms = AllowAny() by_username_perms = retrieve_perms update_perms = IsTheSameUser() + partial_update_perms = IsTheSameUser() destroy_perms = IsTheSameUser() list_perms = AllowAny() stats_perms = AllowAny() @@ -52,5 +53,6 @@ class RolesPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() diff --git a/taiga/webhooks/permissions.py b/taiga/webhooks/permissions.py index 7bc58ef6..40463642 100644 --- a/taiga/webhooks/permissions.py +++ b/taiga/webhooks/permissions.py @@ -29,6 +29,7 @@ class WebhookPermission(TaigaResourcePermission): retrieve_perms = IsProjectOwner() create_perms = IsProjectOwner() update_perms = IsProjectOwner() + partial_update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() list_perms = AllowAny() test_perms = IsProjectOwner() From 139d22ff0376aa196ba7a329944034e8eba53c03 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 4 Aug 2015 13:02:17 +0200 Subject: [PATCH 098/190] Issue #3117 - [Dev] After add task in bulk to a closed US, it is not reopened --- taiga/projects/tasks/apps.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/taiga/projects/tasks/apps.py b/taiga/projects/tasks/apps.py index 445ca63b..8d9a112d 100644 --- a/taiga/projects/tasks/apps.py +++ b/taiga/projects/tasks/apps.py @@ -23,19 +23,6 @@ from taiga.projects.custom_attributes import signals as custom_attributes_handle from . import signals as handlers def connect_tasks_signals(): - # Cached prev object version - signals.pre_save.connect(handlers.cached_prev_task, - sender=apps.get_model("tasks", "Task"), - dispatch_uid="cached_prev_task") - - # Open/Close US and Milestone - signals.post_save.connect(handlers.try_to_close_or_open_us_and_milestone_when_create_or_edit_task, - sender=apps.get_model("tasks", "Task"), - dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") - signals.post_delete.connect(handlers.try_to_close_or_open_us_and_milestone_when_delete_task, - sender=apps.get_model("tasks", "Task"), - dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") - # Tags signals.pre_save.connect(generic_handlers.tags_normalization, sender=apps.get_model("tasks", "Task"), @@ -47,6 +34,18 @@ def connect_tasks_signals(): sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_delete_tagglabe_item_task") +def connect_tasks_close_or_open_us_and_milestone_signals(): + # Cached prev object version + signals.pre_save.connect(handlers.cached_prev_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="cached_prev_task") + # Open/Close US and Milestone + signals.post_save.connect(handlers.try_to_close_or_open_us_and_milestone_when_create_or_edit_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") + signals.post_delete.connect(handlers.try_to_close_or_open_us_and_milestone_when_delete_task, + sender=apps.get_model("tasks", "Task"), + dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") def connect_tasks_custom_attributes_signals(): signals.post_save.connect(custom_attributes_handlers.create_custom_attribute_value_when_create_task, @@ -56,24 +55,29 @@ def connect_tasks_custom_attributes_signals(): def connect_all_tasks_signals(): connect_tasks_signals() + connect_tasks_close_or_open_us_and_milestone_signals() connect_tasks_custom_attributes_signals() def disconnect_tasks_signals(): - signals.pre_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="cached_prev_task") - signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") - signals.post_delete.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") signals.pre_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="tags_normalization") signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_create_or_edit_tagglabe_item") signals.post_delete.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="update_project_tags_when_delete_tagglabe_item") +def disconnect_tasks_close_or_open_us_and_milestone_signals(): + signals.pre_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="cached_prev_task") + signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_create_or_edit_task") + signals.post_delete.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="try_to_close_or_open_us_and_milestone_when_delete_task") + + def disconnect_tasks_custom_attributes_signals(): signals.post_save.disconnect(sender=apps.get_model("tasks", "Task"), dispatch_uid="create_custom_attribute_value_when_create_task") def disconnect_all_tasks_signals(): disconnect_tasks_signals() + disconnect_tasks_close_or_open_us_and_milestone_signals() disconnect_tasks_custom_attributes_signals() From cf630318443634f1509a25e41b634c818e6152a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 25 Aug 2015 15:01:49 +0200 Subject: [PATCH 099/190] [i18n] Add portuguese (Brazil) (pt_BR) translation. --- CHANGELOG.md | 1 + settings/common.py | 2 +- taiga/locale/pt_BR/LC_MESSAGES/django.po | 3420 ++++++++++++++++++++++ 3 files changed, 3422 insertions(+), 1 deletion(-) create mode 100644 taiga/locale/pt_BR/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index ea7f3ce6..04307eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. - i18n. - Add polish (pl) translation. + - Add portuguese (Brazil) (pt_BR) translation. - Add russian (ru) translation. ### Misc diff --git a/settings/common.py b/settings/common.py index 9b8cfcec..e9715241 100644 --- a/settings/common.py +++ b/settings/common.py @@ -128,7 +128,7 @@ LANGUAGES = [ #("pa", "ਪੰਜਾਬੀ"), # Punjabi ("pl", "Polski"), # Polish #("pt", "Português (Portugal)"), # Portuguese - #("pt-br", "Português (Brasil)"), # Brazilian Portuguese + ("pt-br", "Português (Brasil)"), # Brazilian Portuguese #("ro", "Română"), # Romanian ("ru", "Русский"), # Russian #("sk", "Slovenčina"), # Slovak diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po new file mode 100644 index 00000000..a013a6cb --- /dev/null +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -0,0 +1,3420 @@ +# taiga-back.taiga. +# Copyright (C) 2015 Taiga Dev Team +# This file is distributed under the same license as the taiga-back package. +# +# Translators: +# Cléber Zavadniak , 2015 +# Thiago , 2015 +# Kemel Zaidan , 2015 +# Marlon Carvalho , 2015 +# Renato Prado , 2015 +# Thiago , 2015 +msgid "" +msgstr "" +"Project-Id-Version: taiga-back\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-08-24 10:34+0200\n" +"PO-Revision-Date: 2015-08-24 23:00+0000\n" +"Last-Translator: Renato Prado \n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" +"taiga-back/language/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: taiga/auth/api.py:99 +msgid "Public register is disabled." +msgstr "Registro público está desabilitado. " + +#: taiga/auth/api.py:132 +msgid "invalid register type" +msgstr "tipo de registro inválido" + +#: taiga/auth/api.py:145 +msgid "invalid login type" +msgstr "tipo de login inválido" + +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +msgid "invalid username" +msgstr "nome de usuário inválido" + +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +msgid "" +"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" +msgstr "Obrigatório. No máximo 255 caracteres. Letras, números e /./-/_ ." + +#: taiga/auth/services.py:75 +msgid "Username is already in use." +msgstr "Nome de usuário já está em uso." + +#: taiga/auth/services.py:78 +msgid "Email is already in use." +msgstr "Este e-mail já está em uso." + +#: taiga/auth/services.py:94 +msgid "Token not matches any valid invitation." +msgstr "Esse token não bate com nenhum convite." + +#: taiga/auth/services.py:122 +msgid "User is already registered." +msgstr "Este usuário já está registrado." + +#: taiga/auth/services.py:146 +msgid "Membership with user is already exists." +msgstr "Esse usuário já é membro." + +#: taiga/auth/services.py:172 +msgid "Error on creating new user." +msgstr "Erro ao criar um novo usuário." + +#: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +msgid "Invalid token" +msgstr "Token inválido" + +#: taiga/base/api/fields.py:268 +msgid "This field is required." +msgstr "Este campo é obrigatório." + +#: taiga/base/api/fields.py:269 taiga/base/api/relations.py:311 +msgid "Invalid value." +msgstr "Valor inválido." + +#: taiga/base/api/fields.py:453 +#, python-format +msgid "'%s' value must be either True or False." +msgstr "O valor de '%s' deve ser ou True ou False." + +#: taiga/base/api/fields.py:517 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Entre uma 'slug' válida, consistindo de letras, números, underscores ou " +"hífens." + +#: taiga/base/api/fields.py:532 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Escolha uma alternativa válida. %(value)s não está disponível." + +#: taiga/base/api/fields.py:595 +msgid "Enter a valid email address." +msgstr "Preencha com um e-mail válido." + +#: taiga/base/api/fields.py:637 +#, python-format +msgid "Date has wrong format. Use one of these formats instead: %s" +msgstr "A data está no formato errado. Use um desses no lugar: %s" + +#: taiga/base/api/fields.py:701 +#, python-format +msgid "Datetime has wrong format. Use one of these formats instead: %s" +msgstr "Formato da data e hora errado. Use um destes: %s" + +#: taiga/base/api/fields.py:771 +#, python-format +msgid "Time has wrong format. Use one of these formats instead: %s" +msgstr "Hora com formato errado. Use um destes: %s" + +#: taiga/base/api/fields.py:828 +msgid "Enter a whole number." +msgstr "Insira um número inteiro." + +#: taiga/base/api/fields.py:829 taiga/base/api/fields.py:882 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Garanta que o valor é menor ou igual a %(limit_value)s." + +#: taiga/base/api/fields.py:830 taiga/base/api/fields.py:883 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Garanta que o valor é maior ou igual a %(limit_value)s." + +#: taiga/base/api/fields.py:860 +#, python-format +msgid "\"%s\" value must be a float." +msgstr "O valor de \"%s\" deve ser decimal (float)." + +#: taiga/base/api/fields.py:881 +msgid "Enter a number." +msgstr "Insira um número." + +#: taiga/base/api/fields.py:884 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "Garanta que não há mais que %s dígitos no total." + +#: taiga/base/api/fields.py:885 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "Garanta que não há mais que %s casas decimais." + +#: taiga/base/api/fields.py:886 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "Garanta que não há mais que %s dígitos antes do ponto decimal." + +#: taiga/base/api/fields.py:953 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "Nenhum arquivo enviado. Verifique o tipo de codificação no formulário." + +#: taiga/base/api/fields.py:954 +msgid "No file was submitted." +msgstr "Nenhum arquivo enviado." + +#: taiga/base/api/fields.py:955 +msgid "The submitted file is empty." +msgstr "O arquivo enviado está vazio." + +#: taiga/base/api/fields.py:956 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "" +"Garanta que o nome do arquivo tem no máximo %(max)d caracteres (no momento " +"tem %(length)d)." + +#: taiga/base/api/fields.py:957 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "Envie um arquivo ou marque o checkbox \"vazio\", não ambos." + +#: taiga/base/api/fields.py:997 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Envie uma imagem válida. O arquivo que você mandou ou não era uma imagem ou " +"está corrompido." + +#: taiga/base/api/pagination.py:115 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "Página não é \"última\", nem pode ser convertída para um inteiro." + +#: taiga/base/api/pagination.py:119 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Página inválida (%(page_number)s): %(message)s" + +#: taiga/base/api/permissions.py:61 +msgid "Invalid permission definition." +msgstr "Definição de permissão inválida." + +#: taiga/base/api/relations.py:221 +#, python-format +msgid "Invalid pk '%s' - object does not exist." +msgstr "Chave primária '%s' inválida - objeto não existe." + +#: taiga/base/api/relations.py:222 +#, python-format +msgid "Incorrect type. Expected pk value, received %s." +msgstr "Tipo incorreto. Esperado valor de chave primária, recebido %s." + +#: taiga/base/api/relations.py:310 +#, python-format +msgid "Object with %s=%s does not exist." +msgstr "Objeto com %s=%s não existe." + +#: taiga/base/api/relations.py:346 +msgid "Invalid hyperlink - No URL match" +msgstr "Hyperlink inválido - Nenhuma URL casa" + +#: taiga/base/api/relations.py:347 +msgid "Invalid hyperlink - Incorrect URL match" +msgstr "Hyperlink inválido - Casamento de URL incorreto" + +#: taiga/base/api/relations.py:348 +msgid "Invalid hyperlink due to configuration error" +msgstr "Hyperlink inválido devido a erro de configuração" + +#: taiga/base/api/relations.py:349 +msgid "Invalid hyperlink - object does not exist." +msgstr "Hyperlink inválido - objeto não existe." + +#: taiga/base/api/relations.py:350 +#, python-format +msgid "Incorrect type. Expected url string, received %s." +msgstr "Tipo incorreto. Esperada string de url, recebido %s." + +#: taiga/base/api/serializers.py:296 +msgid "Invalid data" +msgstr "Dados inválidos" + +#: taiga/base/api/serializers.py:388 +msgid "No input provided" +msgstr "Nenhuma entrada providenciada" + +#: taiga/base/api/serializers.py:548 +msgid "Cannot create a new item, only existing items may be updated." +msgstr "" +"Não é possível criar um novo item. Somente itens já existentes podem ser " +"atualizados." + +#: taiga/base/api/serializers.py:559 +msgid "Expected a list of items." +msgstr "Esperada uma lista de itens." + +#: taiga/base/api/views.py:100 +msgid "Not found" +msgstr "Não encontrado" + +#: taiga/base/api/views.py:103 +msgid "Permission denied" +msgstr "Permissão negada" + +#: taiga/base/api/views.py:451 +msgid "Server application error" +msgstr "Erro no servidor da aplicação" + +#: taiga/base/connectors/exceptions.py:24 +msgid "Connection error." +msgstr "Erro na conexão." + +#: taiga/base/exceptions.py:53 +msgid "Malformed request." +msgstr "Requisição mal-formada" + +#: taiga/base/exceptions.py:58 +msgid "Incorrect authentication credentials." +msgstr "Credenciais de autenticação incorretas." + +#: taiga/base/exceptions.py:63 +msgid "Authentication credentials were not provided." +msgstr "Credenciais de autenticação não informadas." + +#: taiga/base/exceptions.py:68 +msgid "You do not have permission to perform this action." +msgstr "Você não possui permissão para executar esta ação." + +#: taiga/base/exceptions.py:73 +#, python-format +msgid "Method '%s' not allowed." +msgstr "Método '%s' não é permitido" + +#: taiga/base/exceptions.py:81 +msgid "Could not satisfy the request's Accept header" +msgstr "Não foi possível satisfazer o cabeçalho Accept da requisição" + +#: taiga/base/exceptions.py:90 +#, python-format +msgid "Unsupported media type '%s' in request." +msgstr "Tipo de mídia '%s' não suportado na requisição." + +#: taiga/base/exceptions.py:98 +msgid "Request was throttled." +msgstr "Requisição foi sujeita a limites." + +#: taiga/base/exceptions.py:99 +#, python-format +msgid "Expected available in %d second%s." +msgstr "Esperado disponível em %d segundo%s." + +#: taiga/base/exceptions.py:113 +msgid "Unexpected error" +msgstr "Erro inesperado" + +#: taiga/base/exceptions.py:125 +msgid "Not found." +msgstr "Não encontrado." + +#: taiga/base/exceptions.py:130 +msgid "Method not supported for this endpoint." +msgstr "Método não suportado por esse endpoint." + +#: taiga/base/exceptions.py:138 taiga/base/exceptions.py:146 +msgid "Wrong arguments." +msgstr "Argumentos errados." + +#: taiga/base/exceptions.py:150 +msgid "Data validation error" +msgstr "Erro de validação dos dados" + +#: taiga/base/exceptions.py:162 +msgid "Integrity Error for wrong or invalid arguments" +msgstr "Erro de Integridade para argumentos inválidos ou errados" + +#: taiga/base/exceptions.py:169 +msgid "Precondition error" +msgstr "Erro de pré-condição" + +#: taiga/base/filters.py:79 +msgid "Error in filter params types." +msgstr "Erro nos tipos de parâmetros do filtro." + +#: taiga/base/filters.py:133 taiga/base/filters.py:222 +#: taiga/base/filters.py:271 +msgid "'project' must be an integer value." +msgstr "'projeto' deve ser um valor inteiro." + +#: taiga/base/tags.py:25 +msgid "tags" +msgstr "tags" + +#: taiga/base/templates/emails/base-body-html.jinja:6 +msgid "Taiga" +msgstr "Taiga" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Follow us on Twitter" +msgstr "Siga-nos no Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Twitter" +msgstr "Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "Get the code on GitHub" +msgstr "Pegue o código no GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "GitHub" +msgstr "GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Visit our website" +msgstr "Visite o nosso website" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Taiga.io" +msgstr "Taiga.io" + +#: taiga/base/templates/emails/base-body-html.jinja:423 +#: taiga/base/templates/emails/hero-body-html.jinja:397 +#: taiga/base/templates/emails/updates-body-html.jinja:459 +#, python-format +msgid "" +"\n" +" Taiga Support:\n" +" %(support_url)s\n" +"
\n" +" Contact us:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Mailing list:\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " +msgstr "" +"\n" +" Suporte Taiga:\n" +" %(support_url)s\n" +"
\n" +" Nos comunique:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Lista de e-mail:" +"\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " + +#: taiga/base/templates/emails/hero-body-html.jinja:6 +msgid "You have been Taigatized" +msgstr "Você fpoi Taigueado" + +#: taiga/base/templates/emails/hero-body-html.jinja:359 +msgid "" +"\n" +"

You have been Taigatized!" +"

\n" +"

Welcome to Taiga, an Open " +"Source, Agile Project Management Tool

\n" +" " +msgstr "" +"\n" +"

Você foi taigueado!\n" +"

Bem vindo ao Taiga, " +"ferramenta de gerenciamento de projeto ágil e Open Source

\n" +" " + +#: taiga/base/templates/emails/updates-body-html.jinja:6 +msgid "[Taiga] Updates" +msgstr "[Taiga] Atualizações" + +#: taiga/base/templates/emails/updates-body-html.jinja:417 +msgid "Updates" +msgstr "Atualizações" + +#: taiga/base/templates/emails/updates-body-html.jinja:423 +#, python-format +msgid "" +"\n" +"

comment:" +"

\n" +"

" +"%(comment)s

\n" +" " +msgstr "" +"\n" +"

comentário:" +"

\n" +"

" +"%(comment)s

\n" +" " + +#: taiga/base/templates/emails/updates-body-text.jinja:6 +#, python-format +msgid "" +"\n" +" Comment: %(comment)s\n" +" " +msgstr "" +"\n" +" Comentário: %(comment)s\n" +" " + +#: taiga/export_import/api.py:103 +msgid "We needed at least one role" +msgstr "Nós precisamos de pelo menos uma função" + +#: taiga/export_import/api.py:197 +msgid "Needed dump file" +msgstr "Preciso de arquivo de restauração" + +#: taiga/export_import/api.py:204 +msgid "Invalid dump format" +msgstr "formato de aquivo de restauração inválido" + +#: taiga/export_import/dump_service.py:96 +msgid "error importing project data" +msgstr "erro ao importar informações de projeto" + +#: taiga/export_import/dump_service.py:109 +msgid "error importing lists of project attributes" +msgstr "erro importando lista de atributos do projeto" + +#: taiga/export_import/dump_service.py:114 +msgid "error importing default project attributes values" +msgstr "erro importando valores de atributos do projeto padrão" + +#: taiga/export_import/dump_service.py:124 +msgid "error importing custom attributes" +msgstr "erro importando atributos personalizados" + +#: taiga/export_import/dump_service.py:129 +msgid "error importing roles" +msgstr "erro importando funcões" + +#: taiga/export_import/dump_service.py:144 +msgid "error importing memberships" +msgstr "erro importando filiações" + +#: taiga/export_import/dump_service.py:149 +msgid "error importing sprints" +msgstr "erro importando sprints" + +#: taiga/export_import/dump_service.py:154 +msgid "error importing wiki pages" +msgstr "erro importando páginas wiki" + +#: taiga/export_import/dump_service.py:159 +msgid "error importing wiki links" +msgstr "erro importando wiki links" + +#: taiga/export_import/dump_service.py:164 +msgid "error importing issues" +msgstr "erro importando casos" + +#: taiga/export_import/dump_service.py:169 +msgid "error importing user stories" +msgstr "erro importando user stories" + +#: taiga/export_import/dump_service.py:174 +msgid "error importing tasks" +msgstr "erro importando tarefas" + +#: taiga/export_import/dump_service.py:179 +msgid "error importing tags" +msgstr "erro importando tags" + +#: taiga/export_import/dump_service.py:183 +msgid "error importing timelines" +msgstr "erro importando linha do tempo" + +#: taiga/export_import/serializers.py:161 +msgid "{}=\"{}\" not found in this project" +msgstr "{}=\"{}\" não encontrado nesse projeto" + +#: taiga/export_import/serializers.py:384 +#: taiga/projects/custom_attributes/serializers.py:103 +msgid "Invalid content. It must be {\"key\": \"value\",...}" +msgstr "conteúdo inválido. Deve ser {\"key\": \"value\",...}" + +#: taiga/export_import/serializers.py:399 +#: taiga/projects/custom_attributes/serializers.py:118 +msgid "It contain invalid custom fields." +msgstr "Contém campos personalizados inválidos" + +#: taiga/export_import/serializers.py:468 +#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 +#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 +#: taiga/projects/serializers.py:167 +msgid "Name duplicated for the project" +msgstr "Nome duplicado para o projeto" + +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +msgid "Error generating project dump" +msgstr "Erro gerando arquivo de restauração do projeto" + +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +msgid "Error loading project dump" +msgstr "Erro carregando arquivo de restauração do projeto" + +#: taiga/export_import/templates/emails/dump_project-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump generated

\n" +"

Hello %(user)s,

\n" +"

Your dump from project %(project)s has been correctly generated.\n" +"

You can download it here:

\n" +" Download the dump file\n" +"

This file will be deleted on %(deletion_date)s.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Restauração do projeto gerado

\n" +"

Bem vindo %(user)s,

\n" +"

Seu arquivo de restauração de projeto %(project)s foi corretamente " +"gerado.

\n" +"

Você pode baixa-lo aqui:

\n" +" Download do arquivo de restauração\n" +"

Esse arquivo será deletado em %(deletion_date)s.

\n" +"

O time Taiga

\n" +" " + +#: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your dump from project %(project)s has been correctly generated. You can " +"download it here:\n" +"\n" +"%(url)s\n" +"\n" +"This file will be deleted on %(deletion_date)s.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(user)s,\n" +"\n" +"Seu arquivo de restauração do projeto %(project)s foi gerado corretamente. " +"Você pode baixar ele aqui:\n" +"\n" +"%(url)s\n" +"\n" +"Esse arquivo será deletado em %(deletion_date)s.\n" +"\n" +"---\n" +"O time Taiga\n" + +#: taiga/export_import/templates/emails/dump_project-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been generated" +msgstr "[%(project)s] Seu arquivo de restauração do projeto foi criado" + +#: taiga/export_import/templates/emails/export_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project %(project)s has not been exported correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Olá %(user)s,

\n" +"

Seu projeto %(project)s não foi importado corretamente.

\n" +"

Os administradores de sistema Taiga foram informados.
Por favor, " +"tente novamente ou entre em contato com o time de suporte em\n" +" %(support_email)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/export_import/templates/emails/export_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"Your project %(project)s has not been exported correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(user)s,\n" +"\n" +"%(error_message)s\n" +"Seu projeto %(project)s não foi importado corretamente.\n" +"\n" +"O time de administradores de sistema Taiga foram informados.\n" +"\n" +"Por favor, tente novamente ou contate o time de suporte em " +"%(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" + +#: taiga/export_import/templates/emails/export_error-subject.jinja:1 +#, python-format +msgid "[%(project)s] %(error_subject)s" +msgstr "[%(project)s] %(error_subject)s" + +#: taiga/export_import/templates/emails/import_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project has not been importer correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"

Olá %(user)s,

\n" +"

Seu projeto foi importado corretamente.

\n" +"

O time de administradores de sistema do Taiga foram informados.
" +"Por favor, tentar novamente ou entrar em contado com o time de suporte em\n" +" %(support_email)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/export_import/templates/emails/import_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Your project has not been importer correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Seu projeto não foi importado corretamente.\n" +"\n" +"O time de administradores de sistema do Taiga foram informados.
Por " +"favor, tentar novamente ou entrar em contado com o time de suporte em " +"%(support_email)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/export_import/templates/emails/import_error-subject.jinja:1 +#, python-format +msgid "[Taiga] %(error_subject)s" +msgstr "[Taiga] %(error_subject)s" + +#: taiga/export_import/templates/emails/load_dump-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump imported

\n" +"

Hello %(user)s,

\n" +"

Your project dump has been correctly imported.

\n" +" Go to %(project)s\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Restauração do projeto importada

\n" +"

Olá %(user)s,

\n" +"

Seu arquivo de restauração foi importado corretamente.

\n" +" Ir para %(project)s\n" +"

O Time Taiga

\n" +" " + +#: taiga/export_import/templates/emails/load_dump-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your project dump has been correctly imported.\n" +"\n" +"You can see the project %(project)s here:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(user)s,\n" +"\n" +"Sua restauração foi corretamente importada.\n" +"\n" +"Você pode ver seu projeto %(project)s aqui:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/export_import/templates/emails/load_dump-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been imported" +msgstr "[%(project)s] A restauração do seu projeto foi importada" + +#: taiga/feedback/models.py:23 taiga/users/models.py:111 +msgid "full name" +msgstr "nome completo" + +#: taiga/feedback/models.py:25 taiga/users/models.py:106 +msgid "email address" +msgstr "endereço de e-mail" + +#: taiga/feedback/models.py:27 +msgid "comment" +msgstr "comentário" + +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 +#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 +#: taiga/projects/models.py:129 taiga/projects/models.py:561 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +msgid "created date" +msgstr "data de criação" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Feedback

\n" +"

Taiga has received feedback from %(full_name)s <%(email)s>

\n" +" " +msgstr "" +"\n" +"

Resposta

\n" +"

Taiga recebeu resposta de %(full_name)s <%(email)s>

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:9 +#, python-format +msgid "" +"\n" +"

Comment

\n" +"

%(comment)s

\n" +" " +msgstr "" +"\n" +"

Comentário

\n" +"

%(comment)s

\n" +" " + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 +#: taiga/users/admin.py:51 +msgid "Extra info" +msgstr "Informação extra" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:1 +#, python-format +msgid "" +"---------\n" +"- From: %(full_name)s <%(email)s>\n" +"---------\n" +"- Comment:\n" +"%(comment)s\n" +"---------" +msgstr "" +"---------\n" +"- De: %(full_name)s <%(email)s>\n" +"---------\n" +"- Comentário:\n" +"%(comment)s\n" +"---------" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:8 +msgid "- Extra info:" +msgstr "- Informação extra:" + +#: taiga/feedback/templates/emails/feedback_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Feedback from %(full_name)s <%(email)s>\n" +msgstr "" +"\n" +"[Taiga] Resposta de %(full_name)s <%(email)s>\n" + +#: taiga/hooks/api.py:52 +msgid "The payload is not a valid json" +msgstr "O carregamento não é um json válido" + +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 +#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +msgid "The project doesn't exist" +msgstr "O projeto não existe" + +#: taiga/hooks/api.py:64 +msgid "Bad signature" +msgstr "Bad signature" + +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 +msgid "The referenced element doesn't exist" +msgstr "O elemento referenciado não existe" + +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 +msgid "The status doesn't exist" +msgstr "O estatus não existe" + +#: taiga/hooks/bitbucket/event_hooks.py:86 +msgid "Status changed from BitBucket commit" +msgstr "Status alterado em Bitbucket commit" + +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Informação de caso inválida" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Caso criado por [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "Caso criado pelo Bitbucket." + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Informação de comentário de caso inválido" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Comentário por [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" +"Comentário pelo Bitbucket:\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:96 +#, python-brace-format +msgid "" +"Status changed by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." +msgstr "" +"Status alterado por [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." + +#: taiga/hooks/github/event_hooks.py:107 +msgid "Status changed from GitHub commit." +msgstr "Status alterado por commit do Github." + +#: taiga/hooks/github/event_hooks.py:157 +#, python-brace-format +msgid "" +"Issue created by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Caso criado por [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" + +#: taiga/hooks/github/event_hooks.py:168 +msgid "Issue created from GitHub." +msgstr "Caso criado pelo Github." + +#: taiga/hooks/github/event_hooks.py:200 +#, python-brace-format +msgid "" +"Comment by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Comentário por [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:211 +#, python-brace-format +msgid "" +"Comment From GitHub:\n" +"\n" +"{message}" +msgstr "" +"Comentário pelo Github:\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:86 +msgid "Status changed from GitLab commit" +msgstr "Status alterado por um commit de Gitlab" + +#: taiga/hooks/gitlab/event_hooks.py:128 +msgid "Created from GitLab" +msgstr "Criado pelo Gitlab" + +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Comentário por [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" +"Comentado pelo GitLab:\n" +"\n" +"{message}" + +#: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 +#: taiga/permissions/permissions.py:52 +msgid "View project" +msgstr "Ver projeto" + +#: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 +#: taiga/permissions/permissions.py:54 +msgid "View milestones" +msgstr "" + +#: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 +msgid "View user stories" +msgstr "Ver user stories" + +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:64 +msgid "View tasks" +msgstr "Ver tarefa" + +#: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 +#: taiga/permissions/permissions.py:69 +msgid "View issues" +msgstr "Ver casos" + +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:75 +msgid "View wiki pages" +msgstr "Ver página wiki" + +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 +#: taiga/permissions/permissions.py:80 +msgid "View wiki links" +msgstr "Ver links wiki" + +#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 +msgid "Vote issues" +msgstr "Votar casos" + +#: taiga/permissions/permissions.py:39 +msgid "Request membership" +msgstr "Solicitar filiação" + +#: taiga/permissions/permissions.py:40 +msgid "Add user story to project" +msgstr "Adicionar user story para projeto" + +#: taiga/permissions/permissions.py:41 +msgid "Add comments to user stories" +msgstr "Adicionar comentários para user story" + +#: taiga/permissions/permissions.py:42 +msgid "Add comments to tasks" +msgstr "Adicionar comentário para tarefa" + +#: taiga/permissions/permissions.py:43 +msgid "Add issues" +msgstr "Adicionar casos" + +#: taiga/permissions/permissions.py:44 +msgid "Add comments to issues" +msgstr "Adicionar comentários aos casos" + +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +msgid "Add wiki page" +msgstr "Adicionar página wiki" + +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +msgid "Modify wiki page" +msgstr "modificar página wiki" + +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +msgid "Add wiki link" +msgstr "Adicionar link wiki" + +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +msgid "Modify wiki link" +msgstr "Modificar wiki link" + +#: taiga/permissions/permissions.py:55 +msgid "Add milestone" +msgstr "" + +#: taiga/permissions/permissions.py:56 +msgid "Modify milestone" +msgstr "" + +#: taiga/permissions/permissions.py:57 +msgid "Delete milestone" +msgstr "" + +#: taiga/permissions/permissions.py:59 +msgid "View user story" +msgstr "Ver user story" + +#: taiga/permissions/permissions.py:60 +msgid "Add user story" +msgstr "Adicionar user story" + +#: taiga/permissions/permissions.py:61 +msgid "Modify user story" +msgstr "Modificar user story" + +#: taiga/permissions/permissions.py:62 +msgid "Delete user story" +msgstr "Deletar user story" + +#: taiga/permissions/permissions.py:65 +msgid "Add task" +msgstr "Adicionar tarefa" + +#: taiga/permissions/permissions.py:66 +msgid "Modify task" +msgstr "Modificar tarefa" + +#: taiga/permissions/permissions.py:67 +msgid "Delete task" +msgstr "Deletar tarefa" + +#: taiga/permissions/permissions.py:71 +msgid "Add issue" +msgstr "Adicionar caso" + +#: taiga/permissions/permissions.py:72 +msgid "Modify issue" +msgstr "Modificar caso" + +#: taiga/permissions/permissions.py:73 +msgid "Delete issue" +msgstr "Deletar caso" + +#: taiga/permissions/permissions.py:78 +msgid "Delete wiki page" +msgstr "Deletar página wiki" + +#: taiga/permissions/permissions.py:83 +msgid "Delete wiki link" +msgstr "Deletar link wiki" + +#: taiga/permissions/permissions.py:87 +msgid "Modify project" +msgstr "Modificar projeto" + +#: taiga/permissions/permissions.py:88 +msgid "Add member" +msgstr "adicionar membro" + +#: taiga/permissions/permissions.py:89 +msgid "Remove member" +msgstr "Remover membro" + +#: taiga/permissions/permissions.py:90 +msgid "Delete project" +msgstr "Deletar projeto" + +#: taiga/permissions/permissions.py:91 +msgid "Admin project values" +msgstr "Valores projeto admin" + +#: taiga/permissions/permissions.py:92 +msgid "Admin roles" +msgstr "Funções Admin" + +#: taiga/projects/api.py:198 +msgid "Not valid template name" +msgstr "Nome de template inválido" + +#: taiga/projects/api.py:201 +msgid "Not valid template description" +msgstr "Descrição de template inválida" + +#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +msgid "At least one of the user must be an active admin" +msgstr "Pelo menos one dos usuários deve ser um administrador ativo" + +#: taiga/projects/api.py:499 +msgid "You don't have permisions to see that." +msgstr "Você não tem permissão para ver isso" + +#: taiga/projects/attachments/api.py:47 +msgid "Non partial updates not supported" +msgstr "Updates não parciais não suportados" + +#: taiga/projects/attachments/api.py:62 +msgid "Project ID not matches between object and project" +msgstr "Project ID não encontrado entre objeto e projeto" + +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 +#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/userstorage/models.py:25 +msgid "owner" +msgstr "Dono" + +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 +#: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 +#: taiga/projects/models.py:338 taiga/projects/models.py:364 +#: taiga/projects/models.py:395 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 +#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +msgid "project" +msgstr "projeto" + +#: taiga/projects/attachments/models.py:56 +msgid "content type" +msgstr "tipo de conteudo" + +#: taiga/projects/attachments/models.py:58 +msgid "object id" +msgstr "identidade de objeto" + +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 +#: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 +#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 +#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +msgid "modified date" +msgstr "data modificação" + +#: taiga/projects/attachments/models.py:69 +msgid "attached file" +msgstr "Arquivo anexado" + +#: taiga/projects/attachments/models.py:72 +msgid "is deprecated" +msgstr "está obsoleto" + +#: taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 +#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "descrição" + +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 +#: taiga/projects/models.py:391 taiga/projects/models.py:418 +#: taiga/projects/models.py:453 taiga/projects/models.py:476 +#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +msgid "order" +msgstr "ordem" + +#: taiga/projects/choices.py:21 +msgid "AppearIn" +msgstr "AppearIn" + +#: taiga/projects/choices.py:22 +msgid "Jitsi" +msgstr "Jitsi" + +#: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "personalizado" + +#: taiga/projects/choices.py:24 +msgid "Talky" +msgstr "Talky" + +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "Texto" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "Multi-linha" + +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 +#: taiga/projects/models.py:350 taiga/projects/models.py:389 +#: taiga/projects/models.py:414 taiga/projects/models.py:451 +#: taiga/projects/models.py:474 taiga/projects/models.py:497 +#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/users/models.py:183 taiga/webhooks/models.py:27 +msgid "name" +msgstr "Nome" + +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "Tipo" + +#: taiga/projects/custom_attributes/models.py:87 +msgid "values" +msgstr "valores" + +#: taiga/projects/custom_attributes/models.py:97 +#: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 +msgid "user story" +msgstr "user story" + +#: taiga/projects/custom_attributes/models.py:112 +msgid "task" +msgstr "tarefa" + +#: taiga/projects/custom_attributes/models.py:127 +msgid "issue" +msgstr "caso" + +#: taiga/projects/custom_attributes/serializers.py:57 +msgid "Already exists one with the same name." +msgstr "Já existe um com o mesmo nome." + +#: taiga/projects/history/api.py:70 +msgid "Comment already deleted" +msgstr "Comentário já apagado" + +#: taiga/projects/history/api.py:89 +msgid "Comment not deleted" +msgstr "Comentário não apagado" + +#: taiga/projects/history/choices.py:27 +msgid "Change" +msgstr "Alterar" + +#: taiga/projects/history/choices.py:28 +msgid "Create" +msgstr "Criar" + +#: taiga/projects/history/choices.py:29 +msgid "Delete" +msgstr "Apagar" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:22 +#, python-format +msgid "%(role)s role points" +msgstr "%(role)s pontos de função" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:25 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:130 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:133 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:156 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:193 +msgid "from" +msgstr "de" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:31 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:141 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:144 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:162 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:179 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:199 +msgid "to" +msgstr "para" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:43 +msgid "Added new attachment" +msgstr "Adicionar novos anexos" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:61 +msgid "Updated attachment" +msgstr "Atualizar anexo" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:67 +msgid "deprecated" +msgstr "obsoleto" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:69 +msgid "not deprecated" +msgstr "não-obsoleto" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:85 +msgid "Deleted attachment" +msgstr "Anexo apagado" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:104 +msgid "added" +msgstr "adicionado" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:109 +msgid "removed" +msgstr "removido" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 +#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +msgid "Unassigned" +msgstr "Não-atribuído" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:211 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:86 +msgid "-deleted-" +msgstr "-apagado-" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "to:" +msgstr "para:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "from:" +msgstr "de:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:26 +msgid "Added" +msgstr "Adicionado" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:33 +msgid "Changed" +msgstr "Alterado" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:40 +msgid "Deleted" +msgstr "Apagado" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:54 +msgid "added:" +msgstr "acrescentado:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:57 +msgid "removed:" +msgstr "removido:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:62 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:79 +msgid "From:" +msgstr "De:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:63 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:80 +msgid "To:" +msgstr "Para:" + +#: taiga/projects/history/templatetags/functions.py:26 +#: taiga/projects/wiki/models.py:32 +msgid "content" +msgstr "conteúdo" + +#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/mixins/blocked.py:31 +msgid "blocked note" +msgstr "nota bloqueada" + +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "sprint" + +#: taiga/projects/issues/api.py:157 +msgid "You don't have permissions to set this sprint to this issue." +msgstr "Você não tem permissão para colocar esse sprint para esse caso." + +#: taiga/projects/issues/api.py:161 +msgid "You don't have permissions to set this status to this issue." +msgstr "Você não tem permissão para colocar esse status para esse caso." + +#: taiga/projects/issues/api.py:165 +msgid "You don't have permissions to set this severity to this issue." +msgstr "Você não tem permissão para colocar essa severidade para esse caso." + +#: taiga/projects/issues/api.py:169 +msgid "You don't have permissions to set this priority to this issue." +msgstr "Você não tem permissão para colocar essa prioridade para esse caso." + +#: taiga/projects/issues/api.py:173 +msgid "You don't have permissions to set this type to this issue." +msgstr "Você não tem permissão para colocar esse tipo para esse caso." + +#: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 +#: taiga/projects/userstories/models.py:57 +msgid "ref" +msgstr "ref" + +#: taiga/projects/issues/models.py:40 taiga/projects/tasks/models.py:39 +#: taiga/projects/userstories/models.py:67 +msgid "status" +msgstr "status" + +#: taiga/projects/issues/models.py:42 +msgid "severity" +msgstr "severidade" + +#: taiga/projects/issues/models.py:44 +msgid "priority" +msgstr "prioridade" + +#: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 +#: taiga/projects/userstories/models.py:60 +msgid "milestone" +msgstr "" + +#: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 +msgid "finished date" +msgstr "Data de término" + +#: taiga/projects/issues/models.py:60 taiga/projects/tasks/models.py:53 +#: taiga/projects/userstories/models.py:89 +msgid "subject" +msgstr "Assunto" + +#: taiga/projects/issues/models.py:64 taiga/projects/tasks/models.py:63 +#: taiga/projects/userstories/models.py:93 +msgid "assigned to" +msgstr "Assinado a" + +#: taiga/projects/issues/models.py:66 taiga/projects/tasks/models.py:67 +#: taiga/projects/userstories/models.py:103 +msgid "external reference" +msgstr "referência externa" + +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:416 +#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +msgid "slug" +msgstr "slug" + +#: taiga/projects/milestones/models.py:42 +msgid "estimated start date" +msgstr "Data de início estimada" + +#: taiga/projects/milestones/models.py:43 +msgid "estimated finish date" +msgstr "data de encerramento estimada" + +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 +#: taiga/projects/models.py:420 taiga/projects/models.py:503 +msgid "is closed" +msgstr "está fechado" + +#: taiga/projects/milestones/models.py:52 +msgid "disponibility" +msgstr "disponibilidade" + +#: taiga/projects/milestones/models.py:75 +msgid "The estimated start must be previous to the estimated finish." +msgstr "A estimativa de inicio deve ser anterior a estimativa de encerramento" + +#: taiga/projects/milestones/validators.py:12 +msgid "There's no sprint with that id" +msgstr "Não há sprint com esse id" + +#: taiga/projects/mixins/blocked.py:29 +msgid "is blocked" +msgstr "está bloqueado" + +#: taiga/projects/mixins/ordering.py:47 +#, python-brace-format +msgid "'{param}' parameter is mandatory" +msgstr "'{param}' parametro é mandatório" + +#: taiga/projects/mixins/ordering.py:51 +msgid "'project' parameter is mandatory" +msgstr "'project' parametro é mandatório" + +#: taiga/projects/models.py:59 +msgid "email" +msgstr "email" + +#: taiga/projects/models.py:61 +msgid "create at" +msgstr "criado em" + +#: taiga/projects/models.py:63 taiga/users/models.py:128 +msgid "token" +msgstr "token" + +#: taiga/projects/models.py:69 +msgid "invitation extra text" +msgstr "texto extra de convite" + +#: taiga/projects/models.py:72 +msgid "user order" +msgstr "ordem de usuário" + +#: taiga/projects/models.py:78 +msgid "The user is already member of the project" +msgstr "O usuário já é membro do projeto" + +#: taiga/projects/models.py:93 +msgid "default points" +msgstr "pontos padrão" + +#: taiga/projects/models.py:97 +msgid "default US status" +msgstr "status de US padrão" + +#: taiga/projects/models.py:101 +msgid "default task status" +msgstr "status padrão de tarefa" + +#: taiga/projects/models.py:104 +msgid "default priority" +msgstr "prioridade padrão" + +#: taiga/projects/models.py:107 +msgid "default severity" +msgstr "severidade padrão" + +#: taiga/projects/models.py:111 +msgid "default issue status" +msgstr "status padrão de caso" + +#: taiga/projects/models.py:115 +msgid "default issue type" +msgstr "tipo padrão de caso" + +#: taiga/projects/models.py:136 +msgid "members" +msgstr "members" + +#: taiga/projects/models.py:139 +msgid "total of milestones" +msgstr "" + +#: taiga/projects/models.py:140 +msgid "total story points" +msgstr "pontos totáis de US" + +#: taiga/projects/models.py:143 taiga/projects/models.py:570 +msgid "active backlog panel" +msgstr "painel de backlog ativo" + +#: taiga/projects/models.py:145 taiga/projects/models.py:572 +msgid "active kanban panel" +msgstr "painel de kanban ativo" + +#: taiga/projects/models.py:147 taiga/projects/models.py:574 +msgid "active wiki panel" +msgstr "painel de wiki ativo" + +#: taiga/projects/models.py:149 taiga/projects/models.py:576 +msgid "active issues panel" +msgstr "painel de casos ativo" + +#: taiga/projects/models.py:152 taiga/projects/models.py:579 +msgid "videoconference system" +msgstr "sistema de videoconferencia" + +#: taiga/projects/models.py:154 taiga/projects/models.py:581 +msgid "videoconference extra data" +msgstr "informação extra de videoconferencia" + +#: taiga/projects/models.py:159 +msgid "creation template" +msgstr "template de criação" + +#: taiga/projects/models.py:162 +msgid "anonymous permissions" +msgstr "permissão anônima" + +#: taiga/projects/models.py:166 +msgid "user permissions" +msgstr "permissão de usuário" + +#: taiga/projects/models.py:169 +msgid "is private" +msgstr "é privado" + +#: taiga/projects/models.py:180 +msgid "tags colors" +msgstr "cores de tags" + +#: taiga/projects/models.py:339 +msgid "modules config" +msgstr "configurações de modulos" + +#: taiga/projects/models.py:358 +msgid "is archived" +msgstr "está arquivado" + +#: taiga/projects/models.py:360 taiga/projects/models.py:422 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/users/models.py:113 +msgid "color" +msgstr "cor" + +#: taiga/projects/models.py:362 +msgid "work in progress limit" +msgstr "trabalho no limite de progresso" + +#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +msgid "value" +msgstr "valor" + +#: taiga/projects/models.py:567 +msgid "default owner's role" +msgstr "função padrão para dono " + +#: taiga/projects/models.py:583 +msgid "default options" +msgstr "opções padrão" + +#: taiga/projects/models.py:584 +msgid "us statuses" +msgstr "status de US" + +#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/userstories/models.py:72 +msgid "points" +msgstr "pontos" + +#: taiga/projects/models.py:586 +msgid "task statuses" +msgstr "status de tarefa" + +#: taiga/projects/models.py:587 +msgid "issue statuses" +msgstr "status de casos" + +#: taiga/projects/models.py:588 +msgid "issue types" +msgstr "tipos de caso" + +#: taiga/projects/models.py:589 +msgid "priorities" +msgstr "prioridades" + +#: taiga/projects/models.py:590 +msgid "severities" +msgstr "severidades" + +#: taiga/projects/models.py:591 +msgid "roles" +msgstr "funções" + +#: taiga/projects/notifications/choices.py:28 +msgid "Not watching" +msgstr "não acompanhando" + +#: taiga/projects/notifications/choices.py:29 +msgid "Watching" +msgstr "acompanhando" + +#: taiga/projects/notifications/choices.py:30 +msgid "Ignoring" +msgstr "ignorando" + +#: taiga/projects/notifications/mixins.py:87 +msgid "watchers" +msgstr "visualizadores" + +#: taiga/projects/notifications/models.py:59 +msgid "created date time" +msgstr "data de criação" + +#: taiga/projects/notifications/models.py:61 +msgid "updated date time" +msgstr "data de atualização" + +#: taiga/projects/notifications/models.py:63 +msgid "history entries" +msgstr "histórico de entradas" + +#: taiga/projects/notifications/models.py:66 +msgid "notify users" +msgstr "notificar usuário" + +#: taiga/projects/notifications/services.py:63 +#: taiga/projects/notifications/services.py:77 +msgid "Notify exists for specified user and project" +msgstr "notificação exist para usuário específico e projeto" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue updated

\n" +"

Hello %(user)s,
%(changer)s has updated an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +" " +msgstr "" +"\n" +"

caso atualizado

\n" +"

olá %(user)s,
%(changer)s atualizou caso em %(project)s

\n" +"

caso #%(ref)s %(subject)s

\n" +" Ver caso\n" +"\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Issue updated\n" +"Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New issue created

\n" +"

Hello %(user)s,
%(changer)s has created a new issue on " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

New issue created

\n" +"

Hello %(user)s,
%(changer)s has created a new issue on " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New issue created\n" +"Hello %(user)s, %(changer)s has created a new issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Novo caso criado\n" +"Olá %(user)s, %(changer)s criaram um novo caso em %(project)s\n" +"Ver caso #%(ref)s %(subject)s em %(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Caso deletado

\n" +"

Olá %(user)s,
%(changer)s deletaram um caso em %(project)s

\n" +"

Caso #%(ref)s %(subject)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Issue deleted\n" +"Hello %(user)s, %(changer)s has deleted an issue on %(project)s\n" +"Issue #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Caso deletado\n" +"Olá %(user)s, %(changer)s deletaram um caso em %(project)s\n" +"caso #%(ref)s %(subject)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint updated

\n" +"

Hello %(user)s,
%(changer)s has updated an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See sprint\n" +" " +msgstr "" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Sprint updated\n" +"Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the sprint \"%(milestone)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New sprint created

\n" +"

Hello %(user)s,
%(changer)s has created a new sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See " +"sprint\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Novo sprint criado

\n" +"

Olá %(user)s,
%(changer)s criaram novo sprint em %(project)s\n" +"

Sprint %(name)s

\n" +" Ver " +"sprint\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New sprint created\n" +"Hello %(user)s, %(changer)s has created a new sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Novo sprint criado\n" +"Olá %(user)s, %(changer)s criou um novo sprint on %(project)s\n" +"Ver sprint %(name)s em %(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the sprint \"%(milestone)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Sprint deletado

\n" +"

Olá %(user)s,
%(changer)s deletou sprint em %(project)s

\n" +"

Sprint %(name)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Sprint deleted\n" +"Hello %(user)s, %(changer)s has deleted an sprint on %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Sprint deletado\n" +"Olá %(user)s, %(changer)s deletaram sprint em %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task updated

\n" +"

Hello %(user)s,
%(changer)s has updated a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +" " +msgstr "" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Task updated\n" +"Hello %(user)s, %(changer)s has updated a task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New task created

\n" +"

Hello %(user)s,
%(changer)s has created a new task on " +"%(project)s

\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Nova tarefa criada

\n" +"

Olá %(user)s,
%(changer)s criou nova tarefa em %(project)s

\n" +"

Task #%(ref)s %(subject)s

\n" +" Ver tarefa\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New task created\n" +"Hello %(user)s, %(changer)s has created a new task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Nova tarefa criada\n" +"Olá %(user)s, %(changer)s criou uma nova tarefa em %(project)s\n" +"Ver tarefa #%(ref)s %(subject)s em %(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Tarefa deletada

\n" +"

Olá %(user)s,
%(changer)sdeletaram tarefa em %(project)s

\n" +"

Tarefa #%(ref)s %(subject)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Task deleted\n" +"Hello %(user)s, %(changer)s has deleted a task on %(project)s\n" +"Task #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Tarefa deletada\n" +"Olá %(user)s, %(changer)s deletou tarefa em %(project)s\n" +"Tarefa #%(ref)s %(subject)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story updated

\n" +"

Hello %(user)s,
%(changer)s has updated a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +" " +msgstr "" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"User story updated\n" +"Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New user story created

\n" +"

Hello %(user)s,
%(changer)s has created a new user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Nova user story criada

\n" +"

Olá %(user)s,
%(changer)s criou nova user story em %(project)s\n" +"

User Story #%(ref)s %(subject)s

\n" +" Ver user story\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New user story created\n" +"Hello %(user)s, %(changer)s has created a new user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Nova user story criada\n" +"Olá %(user)s, %(changer)s criou nova user story em %(project)s\n" +"Ver user story #%(ref)s %(subject)s em %(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

User Story deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"User Story deleted\n" +"Hello %(user)s, %(changer)s has deleted a user story on %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"User Story deletada\n" +"Olá %(user)s, %(changer)s deletou user story em %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki Page updated

\n" +"

Hello %(user)s,
%(changer)s has updated a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See Wiki Page\n" +" " +msgstr "" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Wiki Page updated\n" +"\n" +"Hello %(user)s, %(changer)s has updated a wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the Wiki Page \"%(page)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New wiki page created

\n" +"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See " +"wiki page\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

New wiki page created

\n" +"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See " +"wiki page\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New wiki page created\n" +"\n" +"Hello %(user)s, %(changer)s has created a new wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Nova página wiki criada\n" +"\n" +"Olá %(user)s, %(changer)s criou uma página wiki em %(project)s\n" +"\n" +"Ver página wiki %(page)s em %(url)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the Wiki Page \"%(page)s\"\n" +msgstr "" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki page deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Wiki page deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Wiki page deleted\n" +"\n" +"Hello %(user)s, %(changer)s has deleted a wiki page on %(project)s\n" +"\n" +"Wiki page %(page)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Página wiki deletada\n" +"\n" +"Olá %(user)s, %(changer)s deletou página wiki em %(project)s\n" +"\n" +"Página Wiki %(page)s\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" +msgstr "" + +#: taiga/projects/notifications/validators.py:44 +msgid "Watchers contains invalid users" +msgstr "Visualizadores contém usuário inválido" + +#: taiga/projects/occ/mixins.py:35 +msgid "The version must be an integer" +msgstr "A versão precisa ser um inteiro" + +#: taiga/projects/occ/mixins.py:58 +msgid "The version parameter is not valid" +msgstr "A versão do parâmetro não é válida" + +#: taiga/projects/occ/mixins.py:74 +msgid "The version doesn't match with the current one" +msgstr "A versão não verifica com a atual" + +#: taiga/projects/occ/mixins.py:93 +msgid "version" +msgstr "versão" + +#: taiga/projects/permissions.py:39 +msgid "You can't leave the project if there are no more owners" +msgstr "Você não pode deixar o projeto se não há mais donos" + +#: taiga/projects/serializers.py:237 +msgid "Email address is already taken" +msgstr "Endereço de e-mail já utilizado" + +#: taiga/projects/serializers.py:249 +msgid "Invalid role for the project" +msgstr "Função inválida para projeto" + +#: taiga/projects/serializers.py:348 +msgid "Total milestones must be major or equal to zero" +msgstr "" + +#: taiga/projects/serializers.py:405 +msgid "Default options" +msgstr "Opções padrão" + +#: taiga/projects/serializers.py:406 +msgid "User story's statuses" +msgstr "Status de US" + +#: taiga/projects/serializers.py:407 +msgid "Points" +msgstr "Pontos" + +#: taiga/projects/serializers.py:408 +msgid "Task's statuses" +msgstr "Status de tarefas" + +#: taiga/projects/serializers.py:409 +msgid "Issue's statuses" +msgstr "Status de casos" + +#: taiga/projects/serializers.py:410 +msgid "Issue's types" +msgstr "Tipos de casos" + +#: taiga/projects/serializers.py:411 +msgid "Priorities" +msgstr "Prioridades" + +#: taiga/projects/serializers.py:412 +msgid "Severities" +msgstr "Severidades" + +#: taiga/projects/serializers.py:413 +msgid "Roles" +msgstr "Funções" + +#: taiga/projects/services/stats.py:72 +msgid "Future sprint" +msgstr "Sprint futuro" + +#: taiga/projects/services/stats.py:89 +msgid "Project End" +msgstr "Fim do projeto" + +#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +msgid "You don't have permissions to set this sprint to this task." +msgstr "Você não tem permissão para colocar esse sprint para essa tarefa." + +#: taiga/projects/tasks/api.py:100 +msgid "You don't have permissions to set this user story to this task." +msgstr "Você não tem permissão para colocar essa user story para essa tarefa." + +#: taiga/projects/tasks/api.py:103 +msgid "You don't have permissions to set this status to this task." +msgstr "Você não tem permissão para colocar esse status para essa tarefa." + +#: taiga/projects/tasks/models.py:56 +msgid "us order" +msgstr "ordem US" + +#: taiga/projects/tasks/models.py:58 +msgid "taskboard order" +msgstr "ordem de quadro de tarefa" + +#: taiga/projects/tasks/models.py:66 +msgid "is iocaine" +msgstr "é Iocaine" + +#: taiga/projects/tasks/validators.py:12 +msgid "There's no task with that id" +msgstr "Não há tarefas com esse id" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:6 +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:4 +msgid "someone" +msgstr "alguém" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:11 +#, python-format +msgid "" +"\n" +"

You have been invited to Taiga!

\n" +"

Hi! %(full_name)s has sent you an invitation to join project " +"%(project)s in Taiga.
Taiga is a Free, open Source Agile Project " +"Management Tool.

\n" +" " +msgstr "" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 +#, python-format +msgid "" +"\n" +"

And now a few words from the jolly good fellow or sistren
" +"who thought so kindly as to invite you

\n" +"

%(extra)s

\n" +" " +msgstr "" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation to Taiga" +msgstr "Aceita seu convite para o Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation" +msgstr "Aceite seu convite" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:25 +msgid "The Taiga Team" +msgstr "O Time Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:6 +#, python-format +msgid "" +"\n" +"You, or someone you know, has invited you to Taiga\n" +"\n" +"Hi! %(full_name)s has sent you an invitation to join a project called " +"%(project)s which is being managed on Taiga, a Free, open Source Agile " +"Project Management Tool.\n" +msgstr "" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 +#, python-format +msgid "" +"\n" +"And now a few words from the jolly good fellow or sistren who thought so " +"kindly as to invite you:\n" +"\n" +"%(extra)s\n" +" " +msgstr "" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:18 +msgid "Accept your invitation to Taiga following this link:" +msgstr "Aceite seu convite para o Taiga seguindo esse link:" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:20 +msgid "" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/projects/templates/emails/membership_invitation-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Invitation to join to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] convite para se juntar ao projeto '%(project)s'\n" + +#: taiga/projects/templates/emails/membership_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

You have been added to a project

\n" +"

Hello %(full_name)s,
you have been added to the project " +"%(project)s

\n" +" Go to " +"project\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Você foi adicionada ao projeto

\n" +"

Olá %(full_name)s,
você foi adicionado ao projeto %(project)s\n" +" Ir ao " +"projeto\n" +"

O Time Taiga

\n" +" " + +#: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"You have been added to a project\n" +"Hello %(full_name)s,you have been added to the project %(project)s\n" +"\n" +"See project at %(url)s\n" +msgstr "" +"\n" +"Você foi adicionado ao projeto\n" +"Olá %(full_name)s,você foi adicionado ao projeto %(project)s\n" +"\n" +"Ver projeto em %(url)s\n" + +#: taiga/projects/templates/emails/membership_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Added to the project '%(project)s'\n" +msgstr "" +"\n" +"[Taiga] Adicionado ao projeto '%(project)s'\n" + +#. Translators: Name of scrum project template. +#: taiga/projects/translations.py:28 +msgid "Scrum" +msgstr "Scrum" + +#. Translators: Description of scrum project template. +#: taiga/projects/translations.py:30 +msgid "" +"The agile product backlog in Scrum is a prioritized features list, " +"containing short descriptions of all functionality desired in the product. " +"When applying Scrum, it's not necessary to start a project with a lengthy, " +"upfront effort to document all requirements. The Scrum product backlog is " +"then allowed to grow and change as more is learned about the product and its " +"customers" +msgstr "" + +#. Translators: Name of kanban project template. +#: taiga/projects/translations.py:33 +msgid "Kanban" +msgstr "Kanban" + +#. Translators: Description of kanban project template. +#: taiga/projects/translations.py:35 +msgid "" +"Kanban is a method for managing knowledge work with an emphasis on just-in-" +"time delivery while not overloading the team members. In this approach, the " +"process, from definition of a task to its delivery to the customer, is " +"displayed for participants to see and team members pull work from a queue." +msgstr "" + +#. Translators: User story point value (value = undefined) +#: taiga/projects/translations.py:43 +msgid "?" +msgstr "?" + +#. Translators: User story point value (value = 0) +#: taiga/projects/translations.py:45 +msgid "0" +msgstr "0" + +#. Translators: User story point value (value = 0.5) +#: taiga/projects/translations.py:47 +msgid "1/2" +msgstr "1/2" + +#. Translators: User story point value (value = 1) +#: taiga/projects/translations.py:49 +msgid "1" +msgstr "1" + +#. Translators: User story point value (value = 2) +#: taiga/projects/translations.py:51 +msgid "2" +msgstr "2" + +#. Translators: User story point value (value = 3) +#: taiga/projects/translations.py:53 +msgid "3" +msgstr "3" + +#. Translators: User story point value (value = 5) +#: taiga/projects/translations.py:55 +msgid "5" +msgstr "5" + +#. Translators: User story point value (value = 8) +#: taiga/projects/translations.py:57 +msgid "8" +msgstr "8" + +#. Translators: User story point value (value = 10) +#: taiga/projects/translations.py:59 +msgid "10" +msgstr "10" + +#. Translators: User story point value (value = 13) +#: taiga/projects/translations.py:61 +msgid "13" +msgstr "13" + +#. Translators: User story point value (value = 20) +#: taiga/projects/translations.py:63 +msgid "20" +msgstr "20" + +#. Translators: User story point value (value = 40) +#: taiga/projects/translations.py:65 +msgid "40" +msgstr "40" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:73 taiga/projects/translations.py:96 +#: taiga/projects/translations.py:112 +msgid "New" +msgstr "Novo" + +#. Translators: User story status +#: taiga/projects/translations.py:76 +msgid "Ready" +msgstr "Acabado" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:79 taiga/projects/translations.py:98 +#: taiga/projects/translations.py:114 +msgid "In progress" +msgstr "Em andamento" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:82 taiga/projects/translations.py:100 +#: taiga/projects/translations.py:116 +msgid "Ready for test" +msgstr "Pronto para teste" + +#. Translators: User story status +#: taiga/projects/translations.py:85 +msgid "Done" +msgstr "Terminado" + +#. Translators: User story status +#: taiga/projects/translations.py:88 +msgid "Archived" +msgstr "Arquivado" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:102 taiga/projects/translations.py:118 +msgid "Closed" +msgstr "Fechado" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:104 taiga/projects/translations.py:120 +msgid "Needs Info" +msgstr "Precisa de informação" + +#. Translators: Issue status +#: taiga/projects/translations.py:122 +msgid "Postponed" +msgstr "Adiado" + +#. Translators: Issue status +#: taiga/projects/translations.py:124 +msgid "Rejected" +msgstr "Rejeitado" + +#. Translators: Issue type +#: taiga/projects/translations.py:132 +msgid "Bug" +msgstr "Bug" + +#. Translators: Issue type +#: taiga/projects/translations.py:134 +msgid "Question" +msgstr "Pergunta" + +#. Translators: Issue type +#: taiga/projects/translations.py:136 +msgid "Enhancement" +msgstr "Melhoria" + +#. Translators: Issue priority +#: taiga/projects/translations.py:144 +msgid "Low" +msgstr "Baixa" + +#. Translators: Issue priority +#. Translators: Issue severity +#: taiga/projects/translations.py:146 taiga/projects/translations.py:159 +msgid "Normal" +msgstr "Normal" + +#. Translators: Issue priority +#: taiga/projects/translations.py:148 +msgid "High" +msgstr "Alta" + +#. Translators: Issue severity +#: taiga/projects/translations.py:155 +msgid "Wishlist" +msgstr "Desejável" + +#. Translators: Issue severity +#: taiga/projects/translations.py:157 +msgid "Minor" +msgstr "Secundário" + +#. Translators: Issue severity +#: taiga/projects/translations.py:161 +msgid "Important" +msgstr "Importante" + +#. Translators: Issue severity +#: taiga/projects/translations.py:163 +msgid "Critical" +msgstr "Crítica" + +#. Translators: User role +#: taiga/projects/translations.py:170 +msgid "UX" +msgstr "UX" + +#. Translators: User role +#: taiga/projects/translations.py:172 +msgid "Design" +msgstr "Design" + +#. Translators: User role +#: taiga/projects/translations.py:174 +msgid "Front" +msgstr "Front" + +#. Translators: User role +#: taiga/projects/translations.py:176 +msgid "Back" +msgstr "Back" + +#. Translators: User role +#: taiga/projects/translations.py:178 +msgid "Product Owner" +msgstr "Product Owner" + +#. Translators: User role +#: taiga/projects/translations.py:180 +msgid "Stakeholder" +msgstr "Stakeholder" + +#: taiga/projects/userstories/api.py:153 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "Você não tem permissão para colocar esse sprint para essa user story." + +#: taiga/projects/userstories/api.py:157 +msgid "You don't have permissions to set this status to this user story." +msgstr "Você não tem permissão para colocar esse status para essa user story." + +#: taiga/projects/userstories/api.py:251 +#, python-brace-format +msgid "" +"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " +"{subject}\")" +msgstr "" + +#: taiga/projects/userstories/models.py:37 +msgid "role" +msgstr "função" + +#: taiga/projects/userstories/models.py:75 +msgid "backlog order" +msgstr "ordem do backlog" + +#: taiga/projects/userstories/models.py:77 +#: taiga/projects/userstories/models.py:79 +msgid "sprint order" +msgstr "ordem do sprint" + +#: taiga/projects/userstories/models.py:87 +msgid "finish date" +msgstr "data de término" + +#: taiga/projects/userstories/models.py:95 +msgid "is client requirement" +msgstr "É requerimento de cliente" + +#: taiga/projects/userstories/models.py:97 +msgid "is team requirement" +msgstr "É requerimento de time" + +#: taiga/projects/userstories/models.py:102 +msgid "generated from issue" +msgstr "Gerado a partir de caso" + +#: taiga/projects/userstories/validators.py:28 +msgid "There's no user story with that id" +msgstr "Não há user story com esse id" + +#: taiga/projects/validators.py:28 +msgid "There's no project with that id" +msgstr "Não há projeto com esse id" + +#: taiga/projects/validators.py:37 +msgid "There's no user story status with that id" +msgstr "" + +#: taiga/projects/validators.py:46 +msgid "There's no task status with that id" +msgstr "" + +#: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 +#: taiga/projects/votes/models.py:54 +msgid "Votes" +msgstr "Votos" + +#: taiga/projects/votes/models.py:50 +msgid "votes" +msgstr "votos" + +#: taiga/projects/votes/models.py:53 +msgid "Vote" +msgstr "Vote" + +#: taiga/projects/wiki/api.py:60 +msgid "'content' parameter is mandatory" +msgstr "" + +#: taiga/projects/wiki/api.py:63 +msgid "'project_id' parameter is mandatory" +msgstr "" + +#: taiga/projects/wiki/models.py:36 +msgid "last modifier" +msgstr "" + +#: taiga/projects/wiki/models.py:69 +msgid "href" +msgstr "href" + +#: taiga/timeline/signals.py:88 +msgid "Check the history API for the exact diff" +msgstr "" + +#: taiga/users/admin.py:50 +msgid "Personal info" +msgstr "" + +#: taiga/users/admin.py:52 +msgid "Permissions" +msgstr "" + +#: taiga/users/admin.py:53 +msgid "Important dates" +msgstr "" + +#: taiga/users/api.py:124 taiga/users/api.py:131 +msgid "Invalid username or email" +msgstr "" + +#: taiga/users/api.py:140 +msgid "Mail sended successful!" +msgstr "" + +#: taiga/users/api.py:152 taiga/users/api.py:157 +msgid "Token is invalid" +msgstr "" + +#: taiga/users/api.py:178 +msgid "Current password parameter needed" +msgstr "" + +#: taiga/users/api.py:181 +msgid "New password parameter needed" +msgstr "" + +#: taiga/users/api.py:184 +msgid "Invalid password length at least 6 charaters needed" +msgstr "" + +#: taiga/users/api.py:187 +msgid "Invalid current password" +msgstr "" + +#: taiga/users/api.py:203 +msgid "Incomplete arguments" +msgstr "" + +#: taiga/users/api.py:208 +msgid "Invalid image format" +msgstr "" + +#: taiga/users/api.py:261 +msgid "Duplicated email" +msgstr "" + +#: taiga/users/api.py:263 +msgid "Not valid email" +msgstr "" + +#: taiga/users/api.py:283 taiga/users/api.py:289 +msgid "" +"Invalid, are you sure the token is correct and you didn't use it before?" +msgstr "" + +#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +msgid "Invalid, are you sure the token is correct?" +msgstr "" + +#: taiga/users/models.py:69 +msgid "superuser status" +msgstr "" + +#: taiga/users/models.py:70 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" + +#: taiga/users/models.py:100 +msgid "username" +msgstr "usuário" + +#: taiga/users/models.py:101 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" +msgstr "" + +#: taiga/users/models.py:104 +msgid "Enter a valid username." +msgstr "Digite um usuário válido" + +#: taiga/users/models.py:107 +msgid "active" +msgstr "ativo" + +#: taiga/users/models.py:108 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "" + +#: taiga/users/models.py:114 +msgid "biography" +msgstr "biografia" + +#: taiga/users/models.py:117 +msgid "photo" +msgstr "foto" + +#: taiga/users/models.py:118 +msgid "date joined" +msgstr "" + +#: taiga/users/models.py:120 +msgid "default language" +msgstr "" + +#: taiga/users/models.py:122 +msgid "default theme" +msgstr "" + +#: taiga/users/models.py:124 +msgid "default timezone" +msgstr "" + +#: taiga/users/models.py:126 +msgid "colorize tags" +msgstr "" + +#: taiga/users/models.py:131 +msgid "email token" +msgstr "" + +#: taiga/users/models.py:133 +msgid "new email address" +msgstr "novo endereço de email" + +#: taiga/users/models.py:188 +msgid "permissions" +msgstr "permissões" + +#: taiga/users/serializers.py:59 +msgid "invalid" +msgstr "inválido" + +#: taiga/users/serializers.py:70 +msgid "Invalid username. Try with a different one." +msgstr "" + +#: taiga/users/services.py:48 taiga/users/services.py:52 +msgid "Username or password does not matches user." +msgstr "" + +#: taiga/users/templates/emails/change_email-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Change your email

\n" +"

Hello %(full_name)s,
please confirm your email

\n" +" Confirm " +"email\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Mudar seu e-mail

\n" +"

Olá %(full_name)s,
por favor confirmar seu e-mail

\n" +" Confirmar e-mail\n" +"

Desconsidere essa mensagem se não solicitou.

\n" +"

O Time Taiga

\n" +" " + +#: taiga/users/templates/emails/change_email-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, please confirm your email\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(full_name)s, por favor confirmar seu e-mail\n" +"\n" +"%(url)s\n" +"\n" +"Você pode ignorar essa mensagem caso não tenha solicitado\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/users/templates/emails/change_email-subject.jinja:1 +msgid "[Taiga] Change email" +msgstr "" + +#: taiga/users/templates/emails/password_recovery-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Recover your password

\n" +"

Hello %(full_name)s,
you asked to recover your password

\n" +" Recover your password\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

Recupere sua senha

\n" +"

Olá %(full_name)s,
você solicitou para recuperar sua senha\n" +" Recuperar sua senha\n" +"

Você pode ignorar essa mensagem se não solicitou.

\n" +"

O Time Taiga

\n" +" " + +#: taiga/users/templates/emails/password_recovery-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, you asked to recover your password\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Olá %(full_name)s, você solicitiou a alteração de sua senha\n" +"\n" +"%(url)s\n" +"\n" +"Você pode ignorar essa mensagem se não solicitou\n" +"\n" +"---\n" +"O Time Taiga\n" + +#: taiga/users/templates/emails/password_recovery-subject.jinja:1 +msgid "[Taiga] Password recovery" +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:6 +msgid "" +"\n" +" \n" +"

Thank you for registering in Taiga

\n" +"

We hope you enjoy it

\n" +"

We built Taiga because we wanted the project management tool " +"that sits open on our computers all day long, to serve as a continued " +"reminder of why we love to collaborate, code and design.

\n" +"

We built it to be beautiful, elegant, simple to use and fun - " +"without forsaking flexibility and power.

\n" +" The taiga Team\n" +" \n" +" " +msgstr "" +"\n" +" \n" +"

Obrigado por se registrar no Taiga

\n" +"

Esperamos que você goste

\n" +"

Fizemos o taiga porque queriamos uma ferramenta de " +"gerenciamento de projetos que se colocasse aberta em nosso computadores " +"durante o dia, que nos lembrassem porque amamos colaborar, programar e " +"projetar.

\n" +"

Construimos para ser bela, elegante, simples de usar e " +"divertida - sem abrir mão de flexibilidade e poder.

\n" +" O Time Taiga\n" +" \n" +" " + +#: taiga/users/templates/emails/registered_user-body-html.jinja:23 +#, python-format +msgid "" +"\n" +" You may remove your account from this service clicking " +"here\n" +" " +msgstr "" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:1 +msgid "" +"\n" +"Thank you for registering in Taiga\n" +"\n" +"We hope you enjoy it\n" +"\n" +"We built Taiga because we wanted the project management tool that sits open " +"on our computers all day long, to serve as a continued reminder of why we " +"love to collaborate, code and design.\n" +"\n" +"We built it to be beautiful, elegant, simple to use and fun - without " +"forsaking flexibility and power.\n" +"\n" +"--\n" +"The taiga Team\n" +msgstr "" +"\n" +"Obrigado por se registrar no Taiga\n" +"\n" +"Esperamos que você aproveite\n" +"\n" +"Fizemos o taiga porque queriamos uma ferramenta de gerenciamento de projetos " +"que se colocasse aberta em nosso computadores durante o dia, que nos " +"lembrassem porque amamos colaborar, programar e projetar.\n" +"\n" +"Construimos para ser bela, elegante, simples de usar e divertida - sem abrir " +"mão de flexibilidade e poder.\n" +"\n" +"--\n" +"O Time Taiga\n" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:13 +#, python-format +msgid "" +"\n" +"You may remove your account from this service: %(url)s\n" +msgstr "" + +#: taiga/users/templates/emails/registered_user-subject.jinja:1 +msgid "You've been Taigatized!" +msgstr "" + +#: taiga/users/validators.py:29 +msgid "There's no role with that id" +msgstr "" + +#: taiga/userstorage/api.py:50 +msgid "" +"Duplicate key value violates unique constraint. Key '{}' already exists." +msgstr "" + +#: taiga/userstorage/models.py:30 +msgid "key" +msgstr "chave" + +#: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 +msgid "URL" +msgstr "URL" + +#: taiga/webhooks/models.py:29 +msgid "secret key" +msgstr "chave secreta" + +#: taiga/webhooks/models.py:39 +msgid "status code" +msgstr "código de status" + +#: taiga/webhooks/models.py:40 +msgid "request data" +msgstr "dados da requisição" + +#: taiga/webhooks/models.py:41 +msgid "request headers" +msgstr "cabeçalhos da requisição" + +#: taiga/webhooks/models.py:42 +msgid "response data" +msgstr "dados de resposta" + +#: taiga/webhooks/models.py:43 +msgid "response headers" +msgstr "cabeçalhos de resposta" + +#: taiga/webhooks/models.py:44 +msgid "duration" +msgstr "duração" From 44eee5212a082c0833d27aff8dac9f9f5ba22860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 5 Aug 2015 22:22:05 +0200 Subject: [PATCH 100/190] Improve votes module --- CHANGELOG.md | 4 +- taiga/permissions/permissions.py | 12 +- taiga/projects/api.py | 42 ++---- taiga/projects/issues/api.py | 66 ++-------- taiga/projects/issues/permissions.py | 8 +- taiga/projects/issues/serializers.py | 10 +- taiga/projects/permissions.py | 14 +- taiga/projects/serializers.py | 9 +- taiga/projects/tasks/api.py | 16 ++- taiga/projects/tasks/permissions.py | 12 +- taiga/projects/tasks/serializers.py | 4 +- taiga/projects/userstories/api.py | 17 ++- taiga/projects/userstories/permissions.py | 9 ++ taiga/projects/userstories/serializers.py | 4 +- .../migrations/0002_auto_20150805_1600.py | 35 +++++ taiga/projects/votes/mixins/__init__.py | 0 taiga/projects/votes/mixins/serializers.py | 37 ++++++ taiga/projects/votes/mixins/viewsets.py | 114 ++++++++++++++++ taiga/projects/votes/models.py | 10 +- taiga/projects/votes/utils.py | 38 +++++- taiga/routers.py | 13 +- taiga/users/api.py | 9 -- .../test_issues_resources.py | 27 ++-- .../test_projects_resource.py | 71 +++++----- .../test_tasks_resources.py | 91 +++++++++++++ .../test_userstories_resources.py | 90 +++++++++++++ tests/integration/test_star_projects.py | 123 ++++++++++++++++++ tests/integration/test_stars.py | 115 ---------------- tests/integration/test_vote_issues.py | 45 ++++++- tests/integration/test_vote_tasks.py | 123 ++++++++++++++++++ tests/integration/test_vote_userstories.py | 122 +++++++++++++++++ tests/utils.py | 5 +- 32 files changed, 975 insertions(+), 320 deletions(-) create mode 100644 taiga/projects/votes/migrations/0002_auto_20150805_1600.py create mode 100644 taiga/projects/votes/mixins/__init__.py create mode 100644 taiga/projects/votes/mixins/serializers.py create mode 100644 taiga/projects/votes/mixins/viewsets.py create mode 100644 tests/integration/test_star_projects.py delete mode 100644 tests/integration/test_stars.py create mode 100644 tests/integration/test_vote_tasks.py create mode 100644 tests/integration/test_vote_userstories.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 04307eb6..bb640e86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,15 @@ ## 1.9.0 ??? (unreleased) ### Features -- Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)) +- Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)). - Allow multiple actions in the commit messages. - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. - Add custom videoconference system. - Add support for comments in the Gitlab webhooks integration. - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. +- US, tasks and Issues can be upvoted or downvoted and the voters list can be obtained. +- Project can be starred or unstarred and the fans list can be obtained. - i18n. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index b2e516f4..e711c77a 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -32,7 +32,6 @@ USER_PERMISSIONS = [ ('view_milestones', _('View milestones')), ('view_us', _('View user stories')), ('view_issues', _('View issues')), - ('vote_issues', _('Vote issues')), ('view_tasks', _('View tasks')), ('view_wiki_pages', _('View wiki pages')), ('view_wiki_links', _('View wiki links')), @@ -41,15 +40,20 @@ USER_PERMISSIONS = [ ('add_comments_to_us', _('Add comments to user stories')), ('add_comments_to_task', _('Add comments to tasks')), ('add_issue', _('Add issues')), - ('add_comments_issue', _('Add comments to issues')), + ('add_comments_to_issue', _('Add comments to issues')), ('add_wiki_page', _('Add wiki page')), ('modify_wiki_page', _('Modify wiki page')), ('add_wiki_link', _('Add wiki link')), ('modify_wiki_link', _('Modify wiki link')), + ('star_project', _('Star project')), + ('vote_us', _('Vote user story')), + ('vote_task', _('Vote task')), + ('vote_issue', _('Vote issue')), ] MEMBERS_PERMISSIONS = [ ('view_project', _('View project')), + ('star_project', _('Star project')), # Milestone permissions ('view_milestones', _('View milestones')), ('add_milestone', _('Add milestone')), @@ -60,17 +64,19 @@ MEMBERS_PERMISSIONS = [ ('add_us', _('Add user story')), ('modify_us', _('Modify user story')), ('delete_us', _('Delete user story')), + ('vote_us', _('Vote user story')), # Task permissions ('view_tasks', _('View tasks')), ('add_task', _('Add task')), ('modify_task', _('Modify task')), ('delete_task', _('Delete task')), + ('vote_task', _('Vote task')), # Issue permissions ('view_issues', _('View issues')), - ('vote_issues', _('Vote issues')), ('add_issue', _('Add issue')), ('modify_issue', _('Modify issue')), ('delete_issue', _('Delete issue')), + ('vote_issue', _('Vote issue')), # Wiki page permissions ('view_wiki_pages', _('View wiki pages')), ('add_wiki_page', _('Add wiki page')), diff --git a/taiga/projects/api.py b/taiga/projects/api.py index cd70fbf3..9ff9be8c 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -44,15 +44,14 @@ from . import models from . import permissions from . import services -from .votes import serializers as votes_serializers -from .votes import services as votes_service -from .votes.utils import attach_votescount_to_queryset +from .votes.mixins.viewsets import StarredResourceMixin, VotersViewSetMixin ###################################################### ## Project ###################################################### -class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): +class ProjectViewSet(StarredResourceMixin, HistoryResourceMixin, ModelCrudViewSet): + queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer admin_serializer_class = serializers.ProjectDetailAdminSerializer list_serializer_class = serializers.ProjectSerializer @@ -61,6 +60,10 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): filter_fields = (('member', 'members'),) order_by_fields = ("memberships__user_order",) + def get_queryset(self): + qs = super().get_queryset() + return self.attach_votes_attrs_to_queryset(qs) + @list_route(methods=["POST"]) def bulk_update_order(self, request, **kwargs): if self.request.user.is_anonymous(): @@ -74,10 +77,6 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): services.update_projects_order_in_bulk(data, "user_order", request.user) return response.NoContent(data=None) - def get_queryset(self): - qs = models.Project.objects.all() - return attach_votescount_to_queryset(qs, as_field="stars_count") - def get_serializer_class(self): if self.action == "list": return self.list_serializer_class @@ -166,29 +165,6 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): self.check_permissions(request, "tags_colors", project) return response.Ok(dict(project.tags_colors)) - @detail_route(methods=["POST"]) - def star(self, request, pk=None): - project = self.get_object() - self.check_permissions(request, "star", project) - votes_service.add_vote(project, user=request.user) - return response.Ok() - - @detail_route(methods=["POST"]) - def unstar(self, request, pk=None): - project = self.get_object() - self.check_permissions(request, "unstar", project) - votes_service.remove_vote(project, user=request.user) - return response.Ok() - - @detail_route(methods=["GET"]) - def fans(self, request, pk=None): - project = self.get_object() - self.check_permissions(request, "fans", project) - - voters = votes_service.get_voters(project) - voters_data = votes_serializers.VoterSerializer(voters, many=True) - return response.Ok(voters_data.data) - @detail_route(methods=["POST"]) def create_template(self, request, **kwargs): template_name = request.DATA.get('template_name', None) @@ -287,6 +263,10 @@ class ProjectViewSet(HistoryResourceMixin, ModelCrudViewSet): return response.NoContent() +class ProjectFansViewSet(VotersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.ProjectFansPermission,) + resource_model = models.Project + ###################################################### ## Custom values for selectors diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index ee587e8c..6fc78ce3 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -16,7 +16,7 @@ from django.utils.translation import ugettext as _ from django.db.models import Q -from django.http import Http404, HttpResponse +from django.http import HttpResponse from taiga.base import filters from taiga.base import exceptions as exc @@ -33,16 +33,17 @@ from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.models import Project, IssueStatus, Severity, Priority, IssueType from taiga.projects.milestones.models import Milestone -from taiga.projects.votes.utils import attach_votescount_to_queryset -from taiga.projects.votes import services as votes_service -from taiga.projects.votes import serializers as votes_serializers +from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin + from . import models from . import services from . import permissions from . import serializers -class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): +class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, + ModelCrudViewSet): + queryset = models.Issue.objects.all() permission_classes = (permissions.IssuePermission, ) filter_backends = (filters.CanViewIssuesFilterBackend, filters.OwnersFilter, @@ -139,10 +140,9 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, return super().update(request, *args, **kwargs) def get_queryset(self): - qs = models.Issue.objects.all() + qs = super().get_queryset() qs = qs.prefetch_related("attachments") - qs = attach_votescount_to_queryset(qs, as_field="votes_count") - return qs + return self.attach_votes_attrs_to_queryset(qs) def pre_save(self, obj): if not obj.id: @@ -237,51 +237,7 @@ class IssueViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, return response.BadRequest(serializer.errors) - @detail_route(methods=['post']) - def upvote(self, request, pk=None): - issue = get_object_or_404(models.Issue, pk=pk) - self.check_permissions(request, 'upvote', issue) - - votes_service.add_vote(issue, user=request.user) - return response.Ok() - - @detail_route(methods=['post']) - def downvote(self, request, pk=None): - issue = get_object_or_404(models.Issue, pk=pk) - - self.check_permissions(request, 'downvote', issue) - - votes_service.remove_vote(issue, user=request.user) - return response.Ok() - - -class VotersViewSet(ModelListViewSet): - serializer_class = votes_serializers.VoterSerializer - list_serializer_class = votes_serializers.VoterSerializer - permission_classes = (permissions.IssueVotersPermission, ) - - def retrieve(self, request, *args, **kwargs): - pk = kwargs.get("pk", None) - issue_id = kwargs.get("issue_id", None) - issue = get_object_or_404(models.Issue, pk=issue_id) - - self.check_permissions(request, 'retrieve', issue) - - try: - self.object = votes_service.get_voters(issue).get(pk=pk) - except User.DoesNotExist: - raise Http404 - - serializer = self.get_serializer(self.object) - return response.Ok(serializer.data) - - def list(self, request, *args, **kwargs): - issue_id = kwargs.get("issue_id", None) - issue = get_object_or_404(models.Issue, pk=issue_id) - self.check_permissions(request, 'list', issue) - return super().list(request, *args, **kwargs) - - def get_queryset(self): - issue = models.Issue.objects.get(pk=self.kwargs.get("issue_id")) - return votes_service.get_voters(issue) +class IssueVotersViewSet(VotersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.IssueVotersPermission,) + resource_model = models.Issue diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 076b57a0..423866bc 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -31,10 +31,10 @@ class IssuePermission(TaigaResourcePermission): list_perms = AllowAny() filters_data_perms = AllowAny() csv_perms = AllowAny() - upvote_perms = IsAuthenticated() & HasProjectPerm('vote_issues') - downvote_perms = IsAuthenticated() & HasProjectPerm('vote_issues') bulk_create_perms = HasProjectPerm('add_issue') delete_comment_perms= HasProjectPerm('modify_issue') + upvote_perms = IsAuthenticated() & HasProjectPerm('view_issues') + downvote_perms = IsAuthenticated() & HasProjectPerm('view_issues') class HasIssueIdUrlParam(PermissionComponent): @@ -49,8 +49,4 @@ class IssueVotersPermission(TaigaResourcePermission): enought_perms = IsProjectOwner() | IsSuperUser() global_perms = None retrieve_perms = HasProjectPerm('view_issues') - create_perms = HasProjectPerm('add_issue') - update_perms = HasProjectPerm('modify_issue') - partial_update_perms = HasProjectPerm('modify_issue') - destroy_perms = HasProjectPerm('delete_issue') list_perms = HasProjectPerm('view_issues') diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index 2b2723dd..76023cf0 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -19,17 +19,18 @@ from taiga.base.fields import TagsField from taiga.base.fields import PgArrayField from taiga.base.neighbors import NeighborsSerializerMixin - from taiga.mdrender.service import render as mdrender from taiga.projects.validators import ProjectExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicIssueStatusSerializer +from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin + from taiga.users.serializers import UserBasicInfoSerializer from . import models -class IssueSerializer(WatchersValidator, serializers.ModelSerializer): +class IssueSerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): tags = TagsField(required=False) external_reference = PgArrayField(required=False) is_closed = serializers.Field(source="is_closed") @@ -37,7 +38,6 @@ class IssueSerializer(WatchersValidator, serializers.ModelSerializer): generated_user_stories = serializers.SerializerMethodField("get_generated_user_stories") blocked_note_html = serializers.SerializerMethodField("get_blocked_note_html") description_html = serializers.SerializerMethodField("get_description_html") - votes = serializers.SerializerMethodField("get_votes_number") status_extra_info = BasicIssueStatusSerializer(source="status", required=False, read_only=True) assigned_to_extra_info = UserBasicInfoSerializer(source="assigned_to", required=False, read_only=True) owner_extra_info = UserBasicInfoSerializer(source="owner", required=False, read_only=True) @@ -59,10 +59,6 @@ class IssueSerializer(WatchersValidator, serializers.ModelSerializer): def get_description_html(self, obj): return mdrender(obj.project, obj.description) - def get_votes_number(self, obj): - # The "votes_count" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "votes_count", 0) - class IssueListSerializer(IssueSerializer): class Meta: diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index ff152e58..236bc182 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -54,19 +54,25 @@ class ProjectPermission(TaigaResourcePermission): list_perms = AllowAny() stats_perms = HasProjectPerm('view_project') member_stats_perms = HasProjectPerm('view_project') + issues_stats_perms = HasProjectPerm('view_project') regenerate_userstories_csv_uuid_perms = IsProjectOwner() regenerate_issues_csv_uuid_perms = IsProjectOwner() regenerate_tasks_csv_uuid_perms = IsProjectOwner() - star_perms = IsAuthenticated() - unstar_perms = IsAuthenticated() - issues_stats_perms = HasProjectPerm('view_project') tags_perms = HasProjectPerm('view_project') tags_colors_perms = HasProjectPerm('view_project') - fans_perms = HasProjectPerm('view_project') + star_perms = IsAuthenticated() & HasProjectPerm('view_project') + unstar_perms = IsAuthenticated() & HasProjectPerm('view_project') create_template_perms = IsSuperUser() leave_perms = CanLeaveProject() +class ProjectFansPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_project') + list_perms = HasProjectPerm('view_project') + + class MembershipPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index bc1de975..a7e96834 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -40,7 +40,7 @@ from .validators import ProjectExistsValidator from .custom_attributes.serializers import UserStoryCustomAttributeSerializer from .custom_attributes.serializers import TaskCustomAttributeSerializer from .custom_attributes.serializers import IssueCustomAttributeSerializer - +from .votes.mixins.serializers import StarredResourceSerializerMixin ###################################################### ## Custom values for selectors @@ -305,11 +305,10 @@ class ProjectMemberSerializer(serializers.ModelSerializer): ## Projects ###################################################### -class ProjectSerializer(serializers.ModelSerializer): +class ProjectSerializer(StarredResourceSerializerMixin, serializers.ModelSerializer): tags = TagsField(default=[], required=False) anon_permissions = PgArrayField(required=False) public_permissions = PgArrayField(required=False) - stars = serializers.SerializerMethodField("get_stars_number") my_permissions = serializers.SerializerMethodField("get_my_permissions") i_am_owner = serializers.SerializerMethodField("get_i_am_owner") tags_colors = TagsColorsField(required=False) @@ -321,10 +320,6 @@ class ProjectSerializer(serializers.ModelSerializer): exclude = ("last_us_ref", "last_task_ref", "last_issue_ref", "issues_csv_uuid", "tasks_csv_uuid", "userstories_csv_uuid") - def get_stars_number(self, obj): - # The "stars_count" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "stars_count", 0) - def get_my_permissions(self, obj): if "request" in self.context: return get_user_project_permissions(self.context["request"].user, obj) diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 702d0a58..e2262632 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -20,13 +20,14 @@ from taiga.base.api.utils import get_object_or_404 from taiga.base import filters, response from taiga.base import exceptions as exc from taiga.base.decorators import list_route -from taiga.base.api import ModelCrudViewSet +from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.projects.models import Project, TaskStatus from django.http import HttpResponse from taiga.projects.notifications.mixins import WatchedResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.occ import OCCResourceMixin +from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin from . import models @@ -35,8 +36,9 @@ from . import serializers from . import services -class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): - model = models.Task +class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, + ModelCrudViewSet): + queryset = models.Task.objects.all() permission_classes = (permissions.TaskPermission,) filter_backends = (filters.CanViewTasksFilterBackend,) filter_fields = ["user_story", "milestone", "project", "assigned_to", @@ -82,6 +84,9 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, return super().update(request, *args, **kwargs) + def get_queryset(self): + qs = super().get_queryset() + return self.attach_votes_attrs_to_queryset(qs) def pre_save(self, obj): if obj.user_story: @@ -165,3 +170,8 @@ class TaskViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, @list_route(methods=["POST"]) def bulk_update_us_order(self, request, **kwargs): return self._bulk_update_order("us_order", request, **kwargs) + + +class TaskVotersViewSet(VotersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.TaskVotersPermission,) + resource_model = models.Task diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index 2c1fd7b0..af5fbd49 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -15,7 +15,8 @@ # along with this program. If not, see . from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsProjectOwner, AllowAny, IsSuperUser) + IsAuthenticated, IsProjectOwner, AllowAny, + IsSuperUser) class TaskPermission(TaigaResourcePermission): @@ -30,3 +31,12 @@ class TaskPermission(TaigaResourcePermission): csv_perms = AllowAny() bulk_create_perms = HasProjectPerm('add_task') bulk_update_order_perms = HasProjectPerm('modify_task') + upvote_perms = IsAuthenticated() & HasProjectPerm('view_tasks') + downvote_perms = IsAuthenticated() & HasProjectPerm('view_tasks') + + +class TaskVotersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_tasks') + list_perms = HasProjectPerm('view_tasks') diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 6f64e2d5..3df25a77 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -27,12 +27,14 @@ from taiga.projects.milestones.validators import SprintExistsValidator from taiga.projects.tasks.validators import TaskExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicTaskStatusSerializerSerializer +from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin + from taiga.users.serializers import UserBasicInfoSerializer from . import models -class TaskSerializer(WatchersValidator, serializers.ModelSerializer): +class TaskSerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): tags = TagsField(required=False, default=[]) external_reference = PgArrayField(required=False) comment = serializers.SerializerMethodField("get_comment") diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index b364a53c..8ae11d53 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -28,15 +28,15 @@ from taiga.base import exceptions as exc from taiga.base import response from taiga.base import status from taiga.base.decorators import list_route -from taiga.base.api import ModelCrudViewSet +from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api.utils import get_object_or_404 from taiga.projects.notifications.mixins import WatchedResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.occ import OCCResourceMixin - from taiga.projects.models import Project, UserStoryStatus from taiga.projects.history.services import take_snapshot +from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin from . import models from . import permissions @@ -44,8 +44,9 @@ from . import serializers from . import services -class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): - model = models.UserStory +class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, + ModelCrudViewSet): + queryset = models.UserStory.objects.all() permission_classes = (permissions.UserStoryPermission,) filter_backends = (filters.CanViewUsFilterBackend, filters.OwnersFilter, @@ -109,13 +110,13 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi def get_queryset(self): - qs = self.model.objects.all() + qs = super().get_queryset() qs = qs.prefetch_related("role_points", "role_points__points", "role_points__role", "watchers") qs = qs.select_related("milestone", "project") - return qs + return self.attach_votes_attrs_to_queryset(qs) def pre_save(self, obj): # This is very ugly hack, but having @@ -264,3 +265,7 @@ class UserStoryViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMi self.send_notifications(self.object.generated_from_issue, history) return response + +class UserStoryVotersViewSet(VotersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.UserStoryVotersPermission,) + resource_model = models.UserStory diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 3aa548db..00659141 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -30,3 +30,12 @@ class UserStoryPermission(TaigaResourcePermission): csv_perms = AllowAny() bulk_create_perms = IsAuthenticated() & (HasProjectPerm('add_us_to_project') | HasProjectPerm('add_us')) bulk_update_order_perms = HasProjectPerm('modify_us') + upvote_perms = IsAuthenticated() & HasProjectPerm('view_us') + downvote_perms = IsAuthenticated() & HasProjectPerm('view_us') + + +class UserStoryVotersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_us') + list_perms = HasProjectPerm('view_us') diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index a23cd63c..6c3a76cd 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -27,6 +27,8 @@ from taiga.projects.validators import UserStoryStatusExistsValidator from taiga.projects.userstories.validators import UserStoryExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicUserStoryStatusSerializer +from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin + from taiga.users.serializers import UserBasicInfoSerializer from . import models @@ -42,7 +44,7 @@ class RolePointsField(serializers.WritableField): return json.loads(obj) -class UserStorySerializer(WatchersValidator, serializers.ModelSerializer): +class UserStorySerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): tags = TagsField(default=[], required=False) external_reference = PgArrayField(required=False) points = RolePointsField(source="role_points", required=False) diff --git a/taiga/projects/votes/migrations/0002_auto_20150805_1600.py b/taiga/projects/votes/migrations/0002_auto_20150805_1600.py new file mode 100644 index 00000000..c57f526c --- /dev/null +++ b/taiga/projects/votes/migrations/0002_auto_20150805_1600.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.utils.timezone import utc +from django.conf import settings +import datetime + + +class Migration(migrations.Migration): + + dependencies = [ + ('votes', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='vote', + name='created_date', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2015, 8, 5, 16, 0, 40, 158374, tzinfo=utc), verbose_name='created date'), + preserve_default=False, + ), + migrations.AlterField( + model_name='vote', + name='user', + field=models.ForeignKey(related_name='votes', to=settings.AUTH_USER_MODEL, verbose_name='user'), + preserve_default=True, + ), + migrations.AlterField( + model_name='votes', + name='count', + field=models.PositiveIntegerField(default=0, verbose_name='count'), + preserve_default=True, + ), + ] diff --git a/taiga/projects/votes/mixins/__init__.py b/taiga/projects/votes/mixins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/votes/mixins/serializers.py b/taiga/projects/votes/mixins/serializers.py new file mode 100644 index 00000000..96028eaf --- /dev/null +++ b/taiga/projects/votes/mixins/serializers.py @@ -0,0 +1,37 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 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.base.api import serializers + + +class BaseVotedResourceSerializer(serializers.ModelSerializer): + def get_votes_counter(self, obj): + # The "votes_count" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "votes_count", 0) or 0 + + def get_is_voted(self, obj): + # The "is_voted" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "is_voted", False) or False + + +class StarredResourceSerializerMixin(BaseVotedResourceSerializer): + stars = serializers.SerializerMethodField("get_votes_counter") + is_starred = serializers.SerializerMethodField("get_is_voted") + + +class VotedResourceSerializerMixin(BaseVotedResourceSerializer): + votes = serializers.SerializerMethodField("get_votes_counter") + is_voted = serializers.SerializerMethodField("get_is_voted") diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py new file mode 100644 index 00000000..83bf25bd --- /dev/null +++ b/taiga/projects/votes/mixins/viewsets.py @@ -0,0 +1,114 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 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 django.core.exceptions import ObjectDoesNotExist + +from taiga.base import response +from taiga.base.api import viewsets +from taiga.base.api.utils import get_object_or_404 +from taiga.base.decorators import detail_route + +from taiga.projects.votes import serializers +from taiga.projects.votes import services +from taiga.projects.votes.utils import attach_votes_count_to_queryset, attach_is_vote_to_queryset + + +class BaseVotedResource: + # Note: Update get_queryset method: + # def get_queryset(self): + # qs = super().get_queryset() + # return self.attach_votes_attrs_to_queryset(qs) + + def attach_votes_attrs_to_queryset(self, queryset): + qs = attach_votes_count_to_queryset(queryset) + + if self.request.user.is_authenticated(): + qs = attach_is_vote_to_queryset(self.request.user, qs) + + return qs + + def _add_voter(self, permission, request, pk=None): + obj = self.get_object() + self.check_permissions(request, permission, obj) + + services.add_vote(obj, user=request.user) + return response.Ok() + + def _remove_vote(self, permission, request, pk=None): + obj = self.get_object() + self.check_permissions(request, permission, obj) + + services.remove_vote(obj, user=request.user) + return response.Ok() + + +class StarredResourceMixin(BaseVotedResource): + # Note: objects nedd 'star' and 'unstar' permissions. + + @detail_route(methods=["POST"]) + def star(self, request, pk=None): + return self._add_voter("star", request, pk) + + @detail_route(methods=["POST"]) + def unstar(self, request, pk=None): + return self._remove_vote("unstar", request, pk) + + +class VotedResourceMixin(BaseVotedResource): + # Note: objects nedd 'upvote' and 'downvote' permissions. + + @detail_route(methods=["POST"]) + def upvote(self, request, pk=None): + return self._add_voter("upvote", request, pk) + + @detail_route(methods=["POST"]) + def downvote(self, request, pk=None): + return self._remove_vote("downvote", request, pk) + + +class VotersViewSetMixin: + # Is a ModelListViewSet with two required params: permission_classes and resource_model + serializer_class = serializers.VoterSerializer + list_serializer_class = serializers.VoterSerializer + permission_classes = None + resource_model = None + + def retrieve(self, request, *args, **kwargs): + pk = kwargs.get("pk", None) + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'retrieve', resource) + + try: + self.object = services.get_voters(resource).get(pk=pk) + except ObjectDoesNotExist: # or User.DoesNotExist + return response.NotFound() + + serializer = self.get_serializer(self.object) + return response.Ok(serializer.data) + + def list(self, request, *args, **kwargs): + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'list', resource) + + return super().list(request, *args, **kwargs) + + def get_queryset(self): + resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) + return services.get_voters(resource) diff --git a/taiga/projects/votes/models.py b/taiga/projects/votes/models.py index 5457c3ac..7fcf857a 100644 --- a/taiga/projects/votes/models.py +++ b/taiga/projects/votes/models.py @@ -16,16 +16,16 @@ # along with this program. If not, see . from django.conf import settings +from django.contrib.contenttypes import generic from django.db import models from django.utils.translation import ugettext_lazy as _ -from django.contrib.contenttypes import generic class Votes(models.Model): content_type = models.ForeignKey("contenttypes.ContentType") object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey("content_type", "object_id") - count = models.PositiveIntegerField(default=0) + count = models.PositiveIntegerField(null=False, blank=False, default=0, verbose_name=_("count")) class Meta: verbose_name = _("Votes") @@ -44,10 +44,12 @@ class Votes(models.Model): class Vote(models.Model): content_type = models.ForeignKey("contenttypes.ContentType") - object_id = models.PositiveIntegerField(null=False) + object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey("content_type", "object_id") user = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, - related_name="votes", verbose_name=_("votes")) + related_name="votes", verbose_name=_("user")) + created_date = models.DateTimeField(null=False, blank=False, auto_now_add=True, + verbose_name=_("created date")) class Meta: verbose_name = _("Vote") diff --git a/taiga/projects/votes/utils.py b/taiga/projects/votes/utils.py index 20e72b6d..bff72a6a 100644 --- a/taiga/projects/votes/utils.py +++ b/taiga/projects/votes/utils.py @@ -18,7 +18,7 @@ from django.apps import apps -def attach_votescount_to_queryset(queryset, as_field="votes_count"): +def attach_votes_count_to_queryset(queryset, as_field="votes_count"): """Attach votes count to each object of the queryset. Because of laziness of vote objects creation, this makes much simpler and more efficient to @@ -34,8 +34,40 @@ def attach_votescount_to_queryset(queryset, as_field="votes_count"): """ model = queryset.model type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) - sql = ("SELECT coalesce(votes_votes.count, 0) FROM votes_votes " - "WHERE votes_votes.content_type_id = {type_id} AND votes_votes.object_id = {tbl}.id") + sql = ("""SELECT coalesce(votes_votes.count, 0) + FROM votes_votes + WHERE votes_votes.content_type_id = {type_id} + AND votes_votes.object_id = {tbl}.id""") sql = sql.format(type_id=type.id, tbl=model._meta.db_table) qs = queryset.extra(select={as_field: sql}) return qs + + +def attach_is_vote_to_queryset(user, queryset, as_field="is_voted"): + """Attach is_vote boolean to each object of the queryset. + + Because of laziness of vote objects creation, this makes much simpler and more efficient to + access to votes-object and check if the curren user vote it. + + (The other way was to do it in the serializer with some try/except blocks and additional + queries) + + :param user: A users.User object model + :param queryset: A Django queryset object. + :param as_field: Attach the boolean as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = ("""SELECT CASE WHEN (SELECT count(*) + FROM votes_vote + WHERE votes_vote.content_type_id = {type_id} + AND votes_vote.object_id = {tbl}.id + AND votes_vote.user_id = {user_id}) > 0 + THEN TRUE + ELSE FALSE + END""") + sql = sql.format(type_id=type.id, tbl=model._meta.db_table, user_id=user.id) + qs = queryset.extra(select={as_field: sql}) + return qs diff --git a/taiga/routers.py b/taiga/routers.py index 0f8bc675..ff7ceff0 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -1,4 +1,3 @@ - # Copyright (C) 2014 Andrey Antukh # Copyright (C) 2014 Jesús Espino # Copyright (C) 2014 David Barragán @@ -49,6 +48,7 @@ router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notification # Projects & Selectors from taiga.projects.api import ProjectViewSet +from taiga.projects.api import ProjectFansViewSet from taiga.projects.api import MembershipViewSet from taiga.projects.api import InvitationViewSet from taiga.projects.api import UserStoryStatusViewSet @@ -61,6 +61,7 @@ from taiga.projects.api import SeverityViewSet from taiga.projects.api import ProjectTemplateViewSet router.register(r"projects", ProjectViewSet, base_name="projects") +router.register(r"projects/(?P\d+)/fans", ProjectFansViewSet, base_name="project-fans") 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") @@ -124,20 +125,26 @@ router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-atta # Project components from taiga.projects.milestones.api import MilestoneViewSet from taiga.projects.userstories.api import UserStoryViewSet +from taiga.projects.userstories.api import UserStoryVotersViewSet from taiga.projects.tasks.api import TaskViewSet +from taiga.projects.tasks.api import TaskVotersViewSet from taiga.projects.issues.api import IssueViewSet -from taiga.projects.issues.api import VotersViewSet +from taiga.projects.issues.api import IssueVotersViewSet from taiga.projects.wiki.api import WikiViewSet, WikiLinkViewSet router.register(r"milestones", MilestoneViewSet, base_name="milestones") router.register(r"userstories", UserStoryViewSet, base_name="userstories") +router.register(r"userstories/(?P\d+)/voters", UserStoryVotersViewSet, base_name="userstory-voters") router.register(r"tasks", TaskViewSet, base_name="tasks") +router.register(r"tasks/(?P\d+)/voters", TaskVotersViewSet, base_name="task-voters") router.register(r"issues", IssueViewSet, base_name="issues") -router.register(r"issues/(?P\d+)/voters", VotersViewSet, base_name="issue-voters") +router.register(r"issues/(?P\d+)/voters", IssueVotersViewSet, base_name="issue-voters") router.register(r"wiki", WikiViewSet, base_name="wiki") router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links") + + # History & Components from taiga.projects.history.api import UserStoryHistory from taiga.projects.history.api import TaskHistory diff --git a/taiga/users/api.py b/taiga/users/api.py index 33ec137c..0cd8f50c 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -224,15 +224,6 @@ class UsersViewSet(ModelCrudViewSet): user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) - @detail_route(methods=["GET"]) - def starred(self, request, pk=None): - user = self.get_object() - self.check_permissions(request, 'starred', user) - - stars = votes_service.get_voted(user.pk, model=apps.get_model('projects', 'Project')) - stars_data = StarredSerializer(stars, many=True) - return response.Ok(stars_data.data) - #TODO: commit_on_success def partial_update(self, request, *args, **kwargs): """ diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index 43abdeac..f5a90e66 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -477,12 +477,10 @@ def test_issue_action_upvote(client, data): results = helper_test_http_method(client, 'post', public_url, "", users) assert results == [401, 200, 200, 200, 200] - results = helper_test_http_method(client, 'post', private_url1, "", users) assert results == [401, 200, 200, 200, 200] - results = helper_test_http_method(client, 'post', private_url2, "", users) - assert results == [401, 403, 403, 200, 200] + assert results == [404, 404, 404, 200, 200] def test_issue_action_downvote(client, data): @@ -500,18 +498,16 @@ def test_issue_action_downvote(client, data): results = helper_test_http_method(client, 'post', public_url, "", users) assert results == [401, 200, 200, 200, 200] - results = helper_test_http_method(client, 'post', private_url1, "", users) assert results == [401, 200, 200, 200, 200] - results = helper_test_http_method(client, 'post', private_url2, "", users) - assert results == [401, 403, 403, 200, 200] + assert results == [404, 404, 404, 200, 200] def test_issue_voters_list(client, data): - public_url = reverse('issue-voters-list', kwargs={"issue_id": data.public_issue.pk}) - private_url1 = reverse('issue-voters-list', kwargs={"issue_id": data.private_issue1.pk}) - private_url2 = reverse('issue-voters-list', kwargs={"issue_id": data.private_issue2.pk}) + public_url = reverse('issue-voters-list', kwargs={"resource_id": data.public_issue.pk}) + private_url1 = reverse('issue-voters-list', kwargs={"resource_id": data.private_issue1.pk}) + private_url2 = reverse('issue-voters-list', kwargs={"resource_id": data.private_issue2.pk}) users = [ None, @@ -523,21 +519,22 @@ def test_issue_voters_list(client, data): results = helper_test_http_method(client, 'get', public_url, None, users) assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url1, None, users) assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url2, None, users) assert results == [401, 403, 403, 200, 200] def test_issue_voters_retrieve(client, data): add_vote(data.public_issue, data.project_owner) - public_url = reverse('issue-voters-detail', kwargs={"issue_id": data.public_issue.pk, "pk": data.project_owner.pk}) + public_url = reverse('issue-voters-detail', kwargs={"resource_id": data.public_issue.pk, + "pk": data.project_owner.pk}) add_vote(data.private_issue1, data.project_owner) - private_url1 = reverse('issue-voters-detail', kwargs={"issue_id": data.private_issue1.pk, "pk": data.project_owner.pk}) + private_url1 = reverse('issue-voters-detail', kwargs={"resource_id": data.private_issue1.pk, + "pk": data.project_owner.pk}) add_vote(data.private_issue2, data.project_owner) - private_url2 = reverse('issue-voters-detail', kwargs={"issue_id": data.private_issue2.pk, "pk": data.project_owner.pk}) + private_url2 = reverse('issue-voters-detail', kwargs={"resource_id": data.private_issue2.pk, + "pk": data.project_owner.pk}) users = [ None, @@ -549,10 +546,8 @@ def test_issue_voters_retrieve(client, data): results = helper_test_http_method(client, 'get', public_url, None, users) assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url1, None, users) assert results == [200, 200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private_url2, None, users) assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 575e0d62..e485f497 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -198,6 +198,25 @@ def test_project_action_stats(client, data): assert results == [404, 404, 200, 200] +def test_project_action_issues_stats(client, data): + public_url = reverse('projects-issues-stats', kwargs={"pk": data.public_project.pk}) + private1_url = reverse('projects-issues-stats', kwargs={"pk": data.private_project1.pk}) + private2_url = reverse('projects-issues-stats', kwargs={"pk": data.private_project2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_with_perms, + data.project_owner + ] + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private1_url, None, users) + assert results == [200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private2_url, None, users) + assert results == [404, 404, 200, 200] + + def test_project_action_star(client, data): public_url = reverse('projects-star', kwargs={"pk": data.public_project.pk}) private1_url = reverse('projects-star', kwargs={"pk": data.private_project1.pk}) @@ -236,29 +255,10 @@ def test_project_action_unstar(client, data): assert results == [404, 404, 200, 200] -def test_project_action_issues_stats(client, data): - public_url = reverse('projects-issues-stats', kwargs={"pk": data.public_project.pk}) - private1_url = reverse('projects-issues-stats', kwargs={"pk": data.private_project1.pk}) - private2_url = reverse('projects-issues-stats', kwargs={"pk": data.private_project2.pk}) - - users = [ - None, - data.registered_user, - data.project_member_with_perms, - data.project_owner - ] - results = helper_test_http_method(client, 'get', public_url, None, users) - assert results == [200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private1_url, None, users) - assert results == [200, 200, 200, 200] - results = helper_test_http_method(client, 'get', private2_url, None, users) - assert results == [404, 404, 200, 200] - - -def test_project_action_fans(client, data): - public_url = reverse('projects-fans', kwargs={"pk": data.public_project.pk}) - private1_url = reverse('projects-fans', kwargs={"pk": data.private_project1.pk}) - private2_url = reverse('projects-fans', kwargs={"pk": data.private_project2.pk}) +def test_project_fans_list(client, data): + public_url = reverse('project-fans-list', kwargs={"resource_id": data.public_project.pk}) + private1_url = reverse('project-fans-list', kwargs={"resource_id": data.private_project1.pk}) + private2_url = reverse('project-fans-list', kwargs={"resource_id": data.private_project2.pk}) users = [ None, @@ -273,13 +273,16 @@ def test_project_action_fans(client, data): results = helper_test_http_method_and_count(client, 'get', private1_url, None, users) assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)] results = helper_test_http_method_and_count(client, 'get', private2_url, None, users) - assert results == [(404, 1), (404, 1), (404, 1), (200, 2), (200, 2)] + assert results == [(401, 0), (403, 0), (403, 0), (200, 2), (200, 2)] -def test_user_action_starred(client, data): - url1 = reverse('users-starred', kwargs={"pk": data.project_member_without_perms.pk}) - url2 = reverse('users-starred', kwargs={"pk": data.project_member_with_perms.pk}) - url3 = reverse('users-starred', kwargs={"pk": data.project_owner.pk}) +def test_project_fans_retrieve(client, data): + public_url = reverse('project-fans-detail', kwargs={"resource_id": data.public_project.pk, + "pk": data.project_owner.pk}) + private1_url = reverse('project-fans-detail', kwargs={"resource_id": data.private_project1.pk, + "pk": data.project_owner.pk}) + private2_url = reverse('project-fans-detail', kwargs={"resource_id": data.private_project2.pk, + "pk": data.project_owner.pk}) users = [ None, @@ -289,12 +292,12 @@ def test_user_action_starred(client, data): data.project_owner ] - results = helper_test_http_method_and_count(client, 'get', url1, None, users) - assert results == [(200, 0), (200, 0), (200, 0), (200, 0), (200, 0)] - results = helper_test_http_method_and_count(client, 'get', url2, None, users) - assert results == [(200, 3), (200, 3), (200, 3), (200, 3), (200, 3)] - results = helper_test_http_method_and_count(client, 'get', url3, None, users) - assert results == [(200, 3), (200, 3), (200, 3), (200, 3), (200, 3)] + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private1_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private2_url, None, users) + assert results == [401, 403, 403, 200, 200] def test_project_action_create_template(client, data): diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index 9be43c09..b5c3c4f0 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -9,6 +9,7 @@ from taiga.projects.occ import OCCResourceMixin from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals +from taiga.projects.votes.services import add_vote from unittest import mock @@ -416,6 +417,96 @@ def test_task_action_bulk_create(client, data): assert results == [401, 403, 403, 200, 200] +def test_task_action_upvote(client, data): + public_url = reverse('tasks-upvote', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-upvote', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-upvote', kwargs={"pk": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_task_action_downvote(client, data): + public_url = reverse('tasks-downvote', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-downvote', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-downvote', kwargs={"pk": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_task_voters_list(client, data): + public_url = reverse('task-voters-list', kwargs={"resource_id": data.public_task.pk}) + private_url1 = reverse('task-voters-list', kwargs={"resource_id": data.private_task1.pk}) + private_url2 = reverse('task-voters-list', kwargs={"resource_id": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_task_voters_retrieve(client, data): + add_vote(data.public_task, data.project_owner) + public_url = reverse('task-voters-detail', kwargs={"resource_id": data.public_task.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_task1, data.project_owner) + private_url1 = reverse('task-voters-detail', kwargs={"resource_id": data.private_task1.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_task2, data.project_owner) + private_url2 = reverse('task-voters-detail', kwargs={"resource_id": data.private_task2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + def test_tasks_csv(client, data): url = reverse('tasks-csv') csv_public_uuid = data.public_project.tasks_csv_uuid diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index f41d84ee..c257244b 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -9,6 +9,7 @@ from taiga.projects.occ import OCCResourceMixin from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals +from taiga.projects.votes.services import add_vote from unittest import mock @@ -415,6 +416,95 @@ def test_user_story_action_bulk_update_order(client, data): results = helper_test_http_method(client, 'post', url, post_data, users) assert results == [401, 403, 403, 204, 204] +def test_user_story_action_upvote(client, data): + public_url = reverse('userstories-upvote', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-upvote', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-upvote', kwargs={"pk": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_user_story_action_downvote(client, data): + public_url = reverse('userstories-downvote', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-downvote', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-downvote', kwargs={"pk": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_user_story_voters_list(client, data): + public_url = reverse('userstory-voters-list', kwargs={"resource_id": data.public_user_story.pk}) + private_url1 = reverse('userstory-voters-list', kwargs={"resource_id": data.private_user_story1.pk}) + private_url2 = reverse('userstory-voters-list', kwargs={"resource_id": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_user_story_voters_retrieve(client, data): + add_vote(data.public_user_story, data.project_owner) + public_url = reverse('userstory-voters-detail', kwargs={"resource_id": data.public_user_story.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_user_story1, data.project_owner) + private_url1 = reverse('userstory-voters-detail', kwargs={"resource_id": data.private_user_story1.pk, + "pk": data.project_owner.pk}) + add_vote(data.private_user_story2, data.project_owner) + private_url2 = reverse('userstory-voters-detail', kwargs={"resource_id": data.private_user_story2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + def test_user_stories_csv(client, data): url = reverse('userstories-csv') diff --git a/tests/integration/test_star_projects.py b/tests/integration/test_star_projects.py new file mode 100644 index 00000000..ad8d8e08 --- /dev/null +++ b/tests/integration/test_star_projects.py @@ -0,0 +1,123 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_star_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-star", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unstar_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-unstar", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_project_fans(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.VoteFactory.create(content_object=project, user=user) + url = reverse("project-fans-list", args=(project.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_project_fan(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + vote = f.VoteFactory.create(content_object=project, user=user) + url = reverse("project-fans-detail", args=(project.id, vote.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == vote.user.id + + +def test_get_project_stars(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-detail", args=(project.id,)) + + f.VotesFactory.create(content_object=project, count=5) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['stars'] == 5 + + +def test_get_project_is_starred(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.VotesFactory.create(content_object=project) + url_detail = reverse("projects-detail", args=(project.id,)) + url_star = reverse("projects-star", args=(project.id,)) + url_unstar = reverse("projects-unstar", args=(project.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['stars'] == 0 + assert response.data['is_starred'] == False + + response = client.post(url_star) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['stars'] == 1 + assert response.data['is_starred'] == True + + response = client.post(url_unstar) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['stars'] == 0 + assert response.data['is_starred'] == False diff --git a/tests/integration/test_stars.py b/tests/integration/test_stars.py deleted file mode 100644 index eddb03a5..00000000 --- a/tests/integration/test_stars.py +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import pytest -from django.core.urlresolvers import reverse - -from .. import factories as f - -pytestmark = pytest.mark.django_db - - -def test_project_owner_star_project(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create(owner=user) - f.MembershipFactory.create(project=project, is_owner=True, user=user) - url = reverse("projects-star", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_project_owner_unstar_project(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create(owner=user) - f.MembershipFactory.create(project=project, is_owner=True, user=user) - url = reverse("projects-unstar", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_project_member_star_project(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create() - role = f.RoleFactory.create(project=project, permissions=["view_project"]) - f.MembershipFactory.create(project=project, user=user, role=role) - url = reverse("projects-star", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_project_member_unstar_project(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create() - role = f.RoleFactory.create(project=project, permissions=["view_project"]) - f.MembershipFactory.create(project=project, user=user, role=role) - url = reverse("projects-unstar", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_list_project_fans(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - fan = f.VoteFactory.create(content_object=project) - url = reverse("projects-fans", args=(project.id,)) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data[0]['id'] == fan.user.id - - -def test_list_user_starred_projects(client): - user = f.UserFactory.create() - project = f.ProjectFactory() - url = reverse("users-starred", args=(user.id,)) - f.VoteFactory.create(user=user, content_object=project) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data[0]['id'] == project.id - - -def test_get_project_stars(client): - user = f.UserFactory.create() - project = f.ProjectFactory.create(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-detail", args=(project.id,)) - f.VotesFactory.create(content_object=project, count=5) - f.VotesFactory.create(count=3) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data['stars'] == 5 diff --git a/tests/integration/test_vote_issues.py b/tests/integration/test_vote_issues.py index 691ce432..8faca67b 100644 --- a/tests/integration/test_vote_issues.py +++ b/tests/integration/test_vote_issues.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the @@ -51,8 +51,8 @@ def test_list_issue_voters(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) - url = reverse("issue-voters-list", args=(issue.id,)) f.VoteFactory.create(content_object=issue, user=user) + url = reverse("issue-voters-list", args=(issue.id,)) client.login(user) response = client.get(url) @@ -60,7 +60,6 @@ def test_list_issue_voters(client): assert response.status_code == 200 assert response.data[0]['id'] == user.id - def test_get_issue_voter(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) @@ -74,7 +73,6 @@ def test_get_issue_voter(client): assert response.status_code == 200 assert response.data['id'] == vote.user.id - def test_get_issue_votes(client): user = f.UserFactory.create() issue = f.create_issue(owner=user) @@ -88,3 +86,36 @@ def test_get_issue_votes(client): assert response.status_code == 200 assert response.data['votes'] == 5 + + +def test_get_issue_is_voted(client): + user = f.UserFactory.create() + issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + f.VotesFactory.create(content_object=issue) + url_detail = reverse("issues-detail", args=(issue.id,)) + url_upvote = reverse("issues-upvote", args=(issue.id,)) + url_downvote = reverse("issues-downvote", args=(issue.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False + + response = client.post(url_upvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 1 + assert response.data['is_voted'] == True + + response = client.post(url_downvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False diff --git a/tests/integration/test_vote_tasks.py b/tests/integration/test_vote_tasks.py new file mode 100644 index 00000000..a5cdea56 --- /dev/null +++ b/tests/integration/test_vote_tasks.py @@ -0,0 +1,123 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_upvote_task(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-upvote", args=(task.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_downvote_task(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-downvote", args=(task.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_task_voters(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + f.VoteFactory.create(content_object=task, user=user) + url = reverse("task-voters-list", args=(task.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_task_voter(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + vote = f.VoteFactory.create(content_object=task, user=user) + url = reverse("task-voters-detail", args=(task.id, vote.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == vote.user.id + + +def test_get_task_votes(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-detail", args=(task.id,)) + + f.VotesFactory.create(content_object=task, count=5) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['votes'] == 5 + + +def test_get_task_is_voted(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + f.VotesFactory.create(content_object=task) + url_detail = reverse("tasks-detail", args=(task.id,)) + url_upvote = reverse("tasks-upvote", args=(task.id,)) + url_downvote = reverse("tasks-downvote", args=(task.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False + + response = client.post(url_upvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 1 + assert response.data['is_voted'] == True + + response = client.post(url_downvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False diff --git a/tests/integration/test_vote_userstories.py b/tests/integration/test_vote_userstories.py new file mode 100644 index 00000000..ab863df3 --- /dev/null +++ b/tests/integration/test_vote_userstories.py @@ -0,0 +1,122 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_upvote_user_story(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-upvote", args=(user_story.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_downvote_user_story(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-downvote", args=(user_story.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_user_story_voters(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + f.VoteFactory.create(content_object=user_story, user=user) + url = reverse("userstory-voters-list", args=(user_story.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + +def test_get_userstory_voter(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + vote = f.VoteFactory.create(content_object=user_story, user=user) + url = reverse("userstory-voters-detail", args=(user_story.id, vote.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == vote.user.id + + +def test_get_user_story_votes(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-detail", args=(user_story.id,)) + + f.VotesFactory.create(content_object=user_story, count=5) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['votes'] == 5 + + +def test_get_user_story_is_voted(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + f.VotesFactory.create(content_object=user_story) + url_detail = reverse("userstories-detail", args=(user_story.id,)) + url_upvote = reverse("userstories-upvote", args=(user_story.id,)) + url_downvote = reverse("userstories-downvote", args=(user_story.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False + + response = client.post(url_upvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 1 + assert response.data['is_voted'] == True + + response = client.post(url_downvote) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['votes'] == 0 + assert response.data['is_voted'] == False diff --git a/tests/utils.py b/tests/utils.py index 10f1fd9f..96320972 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -16,7 +16,6 @@ # along with this program. If not, see . from django.db.models import signals -from taiga.base.utils import json def signals_switch(): @@ -69,9 +68,9 @@ def helper_test_http_method(client, method, url, data, users, after_each_request def helper_test_http_method_and_count(client, method, url, data, users, after_each_request=None): responses = _helper_test_http_method_responses(client, method, url, data, users, after_each_request) - return list(map(lambda r: (r.status_code, len(json.loads(r.content.decode('utf-8')))), responses)) + return list(map(lambda r: (r.status_code, len(r.data) if isinstance(r.data, list) and 200 <= r.status_code < 300 else 0), responses)) def helper_test_http_method_and_keys(client, method, url, data, users, after_each_request=None): responses = _helper_test_http_method_responses(client, method, url, data, users, after_each_request) - return list(map(lambda r: (r.status_code, set(json.loads(r.content.decode('utf-8')).keys())), responses)) + return list(map(lambda r: (r.status_code, set(r.data.keys() if isinstance(r.data, dict) and 200 <= r.status_code < 300 else [])), responses)) From f3641f5cfbaaf08d97148ef52a4bf1fd6eef9283 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 18 Aug 2015 09:33:23 +0200 Subject: [PATCH 101/190] Refactoring and improving watchers --- CHANGELOG.md | 2 + taiga/base/fields.py | 9 ++ taiga/base/filters.py | 28 ++++ taiga/export_import/serializers.py | 63 +++++++-- taiga/export_import/service.py | 6 + taiga/permissions/service.py | 5 +- taiga/projects/api.py | 6 +- taiga/projects/apps.py | 8 +- taiga/projects/history/freeze_impl.py | 8 +- taiga/projects/issues/api.py | 10 +- .../migrations/0006_remove_issue_watchers.py | 29 ++++ taiga/projects/issues/permissions.py | 2 + taiga/projects/issues/serializers.py | 3 +- .../migrations/0024_auto_20150810_1247.py | 29 ++++ .../0025_remove_project_watchers.py | 28 ++++ taiga/projects/milestones/api.py | 4 +- .../0002_remove_milestone_watchers.py | 29 ++++ taiga/projects/milestones/serializers.py | 4 +- taiga/projects/models.py | 4 +- .../notifications/migrations/0004_watched.py | 35 +++++ taiga/projects/notifications/mixins.py | 125 ++++++++++++++++-- taiga/projects/notifications/models.py | 17 +++ taiga/projects/notifications/services.py | 53 +++++++- taiga/projects/notifications/utils.py | 63 +++++++++ taiga/projects/notifications/validators.py | 5 +- taiga/projects/permissions.py | 2 + taiga/projects/serializers.py | 5 +- taiga/projects/signals.py | 18 --- taiga/projects/tasks/api.py | 8 +- .../migrations/0008_remove_task_watchers.py | 29 ++++ taiga/projects/tasks/permissions.py | 2 + taiga/projects/tasks/serializers.py | 3 +- taiga/projects/userstories/api.py | 13 +- .../0010_remove_userstory_watchers.py | 29 ++++ taiga/projects/userstories/permissions.py | 3 +- taiga/projects/userstories/serializers.py | 3 +- taiga/projects/votes/serializers.py | 2 + .../0002_remove_wikipage_watchers.py | 30 +++++ taiga/projects/wiki/serializers.py | 7 +- taiga/timeline/signals.py | 2 +- .../migrations/0012_auto_20150812_1142.py | 21 +++ taiga/webhooks/serializers.py | 12 +- .../test_issues_resources.py | 42 ++++++ .../test_projects_resource.py | 38 ++++++ .../test_tasks_resources.py | 42 ++++++ .../test_userstories_resources.py | 42 ++++++ tests/integration/test_importer_api.py | 29 +++- tests/integration/test_notifications.py | 18 +-- tests/integration/test_projects.py | 2 +- tests/integration/test_timeline.py | 10 -- tests/integration/test_watch_issues.py | 47 +++++++ tests/integration/test_watch_milestones.py | 123 +++++++++++++++++ tests/integration/test_watch_projects.py | 47 +++++++ tests/integration/test_watch_tasks.py | 47 +++++++ tests/integration/test_watch_userstories.py | 47 +++++++ tests/integration/test_watch_wikipages.py | 123 +++++++++++++++++ 56 files changed, 1304 insertions(+), 117 deletions(-) create mode 100644 taiga/projects/issues/migrations/0006_remove_issue_watchers.py create mode 100644 taiga/projects/migrations/0024_auto_20150810_1247.py create mode 100644 taiga/projects/migrations/0025_remove_project_watchers.py create mode 100644 taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py create mode 100644 taiga/projects/notifications/migrations/0004_watched.py create mode 100644 taiga/projects/notifications/utils.py create mode 100644 taiga/projects/tasks/migrations/0008_remove_task_watchers.py create mode 100644 taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py create mode 100644 taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py create mode 100644 taiga/users/migrations/0012_auto_20150812_1142.py create mode 100644 tests/integration/test_watch_issues.py create mode 100644 tests/integration/test_watch_milestones.py create mode 100644 tests/integration/test_watch_projects.py create mode 100644 tests/integration/test_watch_tasks.py create mode 100644 tests/integration/test_watch_userstories.py create mode 100644 tests/integration/test_watch_wikipages.py diff --git a/CHANGELOG.md b/CHANGELOG.md index bb640e86..05daf163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. - US, tasks and Issues can be upvoted or downvoted and the voters list can be obtained. - Project can be starred or unstarred and the fans list can be obtained. +- Now users can watch public issues, tasks and user stories. +- Add endpoints to show the watchers list for issues, tasks and user stories. - i18n. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. diff --git a/taiga/base/fields.py b/taiga/base/fields.py index a1a8c56c..74b27c10 100644 --- a/taiga/base/fields.py +++ b/taiga/base/fields.py @@ -110,3 +110,12 @@ class TagsColorsField(serializers.WritableField): def from_native(self, data): return list(data.items()) + + + +class WatchersField(serializers.WritableField): + def to_native(self, obj): + return obj + + def from_native(self, data): + return data diff --git a/taiga/base/filters.py b/taiga/base/filters.py index cbfdbdef..e0de384c 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -18,6 +18,7 @@ from functools import reduce import logging from django.apps import apps +from django.contrib.contenttypes.models import ContentType from django.db.models import Q from django.utils.translation import ugettext as _ @@ -451,6 +452,33 @@ class TagsFilter(FilterBackend): return super().filter_queryset(request, queryset, view) + +class WatchersFilter(FilterBackend): + filter_name = 'watchers' + + def __init__(self, filter_name=None): + if filter_name: + self.filter_name = filter_name + + def _get_watchers_queryparams(self, params): + watchers = params.get(self.filter_name, None) + if watchers: + return watchers.split(",") + + return None + + def filter_queryset(self, request, queryset, view): + query_watchers = self._get_watchers_queryparams(request.QUERY_PARAMS) + model = queryset.model + if query_watchers: + WatchedModel = apps.get_model("notifications", "Watched") + watched_type = ContentType.objects.get_for_model(queryset.model) + watched_ids = WatchedModel.objects.filter(content_type=watched_type, user__id__in=query_watchers).values_list("object_id", flat=True) + queryset = queryset.filter(id__in=watched_ids) + + return super().filter_queryset(request, queryset, view) + + ##################################################################### # Text search filters ##################################################################### diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index ec0ed783..98e9d2cf 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -19,6 +19,7 @@ import copy import os from collections import OrderedDict +from django.apps import apps from django.core.files.base import ContentFile from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ValidationError @@ -43,6 +44,7 @@ from taiga.projects.attachments import models as attachments_models from taiga.timeline import models as timeline_models from taiga.timeline import service as timeline_service from taiga.users import models as users_models +from taiga.projects.notifications import services as notifications_services from taiga.projects.votes import services as votes_service from taiga.projects.history import services as history_service @@ -223,6 +225,48 @@ class HistoryDiffField(JsonField): return data +class WatcheableObjectModelSerializer(serializers.ModelSerializer): + watchers = UserRelatedField(many=True, required=False) + + def __init__(self, *args, **kwargs): + self._watchers_field = self.base_fields.pop("watchers", None) + super(WatcheableObjectModelSerializer, self).__init__(*args, **kwargs) + + """ + watchers is not a field from the model so we need to do some magic to make it work like a normal field + It's supposed to be represented as an email list but internally it's treated like notifications.Watched instances + """ + + def restore_object(self, attrs, instance=None): + watcher_field = self.fields.pop("watchers", None) + instance = super(WatcheableObjectModelSerializer, self).restore_object(attrs, instance) + self._watchers = self.init_data.get("watchers", []) + return instance + + def save_watchers(self): + new_watcher_emails = set(self._watchers) + old_watcher_emails = set(notifications_services.get_watchers(self.object).values_list("email", flat=True)) + adding_watcher_emails = list(new_watcher_emails.difference(old_watcher_emails)) + removing_watcher_emails = list(old_watcher_emails.difference(new_watcher_emails)) + + User = apps.get_model("users", "User") + adding_users = User.objects.filter(email__in=adding_watcher_emails) + removing_users = User.objects.filter(email__in=removing_watcher_emails) + + for user in adding_users: + notifications_services.add_watcher(self.object, user) + + for user in removing_users: + notifications_services.remove_watcher(self.object, user) + + self.object.watchers = notifications_services.get_watchers(self.object) + + def to_native(self, obj): + ret = super(WatcheableObjectModelSerializer, self).to_native(obj) + ret["watchers"] = [user.email for user in notifications_services.get_watchers(obj)] + return ret + + class HistoryExportSerializer(serializers.ModelSerializer): user = HistoryUserField() diff = HistoryDiffField(required=False) @@ -243,7 +287,7 @@ class HistoryExportSerializerMixin(serializers.ModelSerializer): def get_history(self, obj): history_qs = history_service.get_history_queryset_by_model_instance(obj, types=(history_models.HistoryType.change, history_models.HistoryType.create,)) - + return HistoryExportSerializer(history_qs, many=True).data @@ -447,9 +491,8 @@ class RolePointsExportSerializer(serializers.ModelSerializer): exclude = ('id', 'user_story') -class MilestoneExportSerializer(serializers.ModelSerializer): +class MilestoneExportSerializer(WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) - watchers = UserRelatedField(many=True, required=False) modified_date = serializers.DateTimeField(required=False) def __init__(self, *args, **kwargs): @@ -475,13 +518,12 @@ class MilestoneExportSerializer(serializers.ModelSerializer): class TaskExportSerializer(CustomAttributesValuesExportSerializerMixin, HistoryExportSerializerMixin, - AttachmentExportSerializerMixin, serializers.ModelSerializer): + AttachmentExportSerializerMixin, WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) status = ProjectRelatedField(slug_field="name") user_story = ProjectRelatedField(slug_field="ref", required=False) milestone = ProjectRelatedField(slug_field="name", required=False) assigned_to = UserRelatedField(required=False) - watchers = UserRelatedField(many=True, required=False) modified_date = serializers.DateTimeField(required=False) class Meta: @@ -493,13 +535,12 @@ class TaskExportSerializer(CustomAttributesValuesExportSerializerMixin, HistoryE class UserStoryExportSerializer(CustomAttributesValuesExportSerializerMixin, HistoryExportSerializerMixin, - AttachmentExportSerializerMixin, serializers.ModelSerializer): + AttachmentExportSerializerMixin, WatcheableObjectModelSerializer): role_points = RolePointsExportSerializer(many=True, required=False) owner = UserRelatedField(required=False) assigned_to = UserRelatedField(required=False) status = ProjectRelatedField(slug_field="name") milestone = ProjectRelatedField(slug_field="name", required=False) - watchers = UserRelatedField(many=True, required=False) modified_date = serializers.DateTimeField(required=False) generated_from_issue = ProjectRelatedField(slug_field="ref", required=False) @@ -512,7 +553,7 @@ class UserStoryExportSerializer(CustomAttributesValuesExportSerializerMixin, His class IssueExportSerializer(CustomAttributesValuesExportSerializerMixin, HistoryExportSerializerMixin, - AttachmentExportSerializerMixin, serializers.ModelSerializer): + AttachmentExportSerializerMixin, WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) status = ProjectRelatedField(slug_field="name") assigned_to = UserRelatedField(required=False) @@ -520,7 +561,6 @@ class IssueExportSerializer(CustomAttributesValuesExportSerializerMixin, History severity = ProjectRelatedField(slug_field="name") type = ProjectRelatedField(slug_field="name") milestone = ProjectRelatedField(slug_field="name", required=False) - watchers = UserRelatedField(many=True, required=False) votes = serializers.SerializerMethodField("get_votes") modified_date = serializers.DateTimeField(required=False) @@ -536,10 +576,9 @@ class IssueExportSerializer(CustomAttributesValuesExportSerializerMixin, History class WikiPageExportSerializer(HistoryExportSerializerMixin, AttachmentExportSerializerMixin, - serializers.ModelSerializer): + WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) last_modifier = UserRelatedField(required=False) - watchers = UserRelatedField(many=True, required=False) modified_date = serializers.DateTimeField(required=False) class Meta: @@ -586,7 +625,7 @@ class TimelineExportSerializer(serializers.ModelSerializer): exclude = ('id', 'project', 'namespace', 'object_id') -class ProjectExportSerializer(serializers.ModelSerializer): +class ProjectExportSerializer(WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) default_points = serializers.SlugRelatedField(slug_field="name", required=False) default_us_status = serializers.SlugRelatedField(slug_field="name", required=False) diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index 40578118..25b9be90 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -71,6 +71,7 @@ def store_project(data): if serialized.is_valid(): serialized.object._importing = True serialized.object.save() + serialized.save_watchers() return serialized add_errors("project", serialized.errors) return None @@ -217,6 +218,7 @@ def store_task(project, data): serialized.object._not_notify = True serialized.save() + serialized.save_watchers() if serialized.object.ref: sequence_name = refs.make_sequence_name(project) @@ -257,6 +259,7 @@ def store_milestone(project, milestone): serialized.object.project = project serialized.object._importing = True serialized.save() + serialized.save_watchers() for task_without_us in milestone.get("tasks_without_us", []): task_without_us["user_story"] = None @@ -320,6 +323,7 @@ def store_wiki_page(project, wiki_page): serialized.object._importing = True serialized.object._not_notify = True serialized.save() + serialized.save_watchers() for attachment in wiki_page.get("attachments", []): store_attachment(project, serialized.object, attachment) @@ -382,6 +386,7 @@ def store_user_story(project, data): serialized.object._not_notify = True serialized.save() + serialized.save_watchers() if serialized.object.ref: sequence_name = refs.make_sequence_name(project) @@ -442,6 +447,7 @@ def store_issue(project, data): serialized.object._not_notify = True serialized.save() + serialized.save_watchers() if serialized.object.ref: sequence_name = refs.make_sequence_name(project) diff --git a/taiga/permissions/service.py b/taiga/permissions/service.py index d9df5bd7..3a79b6ee 100644 --- a/taiga/permissions/service.py +++ b/taiga/permissions/service.py @@ -15,11 +15,12 @@ # 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 Membership, Project from .permissions import OWNERS_PERMISSIONS, MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from django.apps import apps def _get_user_project_membership(user, project): + Membership = apps.get_model("projects", "Membership") if user.is_anonymous(): return None @@ -30,7 +31,7 @@ def _get_user_project_membership(user, project): def _get_object_project(obj): project = None - + Project = apps.get_model("projects", "Project") if isinstance(obj, Project): project = obj elif obj and hasattr(obj, 'project'): diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 9ff9be8c..6223adb0 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -31,6 +31,7 @@ from taiga.base.api.utils import get_object_or_404 from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -50,7 +51,7 @@ from .votes.mixins.viewsets import StarredResourceMixin, VotersViewSetMixin ## Project ###################################################### -class ProjectViewSet(StarredResourceMixin, HistoryResourceMixin, ModelCrudViewSet): +class ProjectViewSet(StarredResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer admin_serializer_class = serializers.ProjectDetailAdminSerializer @@ -62,7 +63,8 @@ class ProjectViewSet(StarredResourceMixin, HistoryResourceMixin, ModelCrudViewSe def get_queryset(self): qs = super().get_queryset() - return self.attach_votes_attrs_to_queryset(qs) + qs = self.attach_votes_attrs_to_queryset(qs) + return self.attach_watchers_attrs_to_queryset(qs) @list_route(methods=["POST"]) def bulk_update_order(self, request, **kwargs): diff --git a/taiga/projects/apps.py b/taiga/projects/apps.py index acdaa7da..06b42de8 100644 --- a/taiga/projects/apps.py +++ b/taiga/projects/apps.py @@ -27,12 +27,7 @@ def connect_memberships_signals(): sender=apps.get_model("projects", "Membership"), dispatch_uid='membership_pre_delete') - # On membership object is deleted, update watchers of all objects relation. - signals.post_delete.connect(handlers.update_watchers_on_membership_post_delete, - sender=apps.get_model("projects", "Membership"), - dispatch_uid='update_watchers_on_membership_post_delete') - - # On membership object is deleted, update watchers of all objects relation. + # On membership object is deleted, update notify policies of all objects relation. signals.post_save.connect(handlers.create_notify_policy, sender=apps.get_model("projects", "Membership"), dispatch_uid='create-notify-policy') @@ -67,7 +62,6 @@ def connect_task_status_signals(): def disconnect_memberships_signals(): signals.pre_delete.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='membership_pre_delete') - signals.post_delete.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='update_watchers_on_membership_post_delete') signals.post_save.disconnect(sender=apps.get_model("projects", "Membership"), dispatch_uid='create-notify-policy') diff --git a/taiga/projects/history/freeze_impl.py b/taiga/projects/history/freeze_impl.py index a591c666..d5ecc800 100644 --- a/taiga/projects/history/freeze_impl.py +++ b/taiga/projects/history/freeze_impl.py @@ -288,7 +288,7 @@ def userstory_freezer(us) -> dict: "milestone": us.milestone_id, "client_requirement": us.client_requirement, "team_requirement": us.team_requirement, - "watchers": [x.id for x in us.watchers.all()], + "watchers": [x.id for x in us.get_watchers()], "attachments": extract_attachments(us), "tags": us.tags, "points": points, @@ -315,7 +315,7 @@ def issue_freezer(issue) -> dict: "description": issue.description, "description_html": mdrender(issue.project, issue.description), "assigned_to": issue.assigned_to_id, - "watchers": [x.pk for x in issue.watchers.all()], + "watchers": [x.pk for x in issue.get_watchers()], "attachments": extract_attachments(issue), "tags": issue.tags, "is_blocked": issue.is_blocked, @@ -337,7 +337,7 @@ def task_freezer(task) -> dict: "description": task.description, "description_html": mdrender(task.project, task.description), "assigned_to": task.assigned_to_id, - "watchers": [x.pk for x in task.watchers.all()], + "watchers": [x.pk for x in task.get_watchers()], "attachments": extract_attachments(task), "taskboard_order": task.taskboard_order, "us_order": task.us_order, @@ -359,7 +359,7 @@ def wikipage_freezer(wiki) -> dict: "owner": wiki.owner_id, "content": wiki.content, "content_html": mdrender(wiki.project, wiki.content), - "watchers": [x.pk for x in wiki.watchers.all()], + "watchers": [x.pk for x in wiki.get_watchers()], "attachments": extract_attachments(wiki), } diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index 6fc78ce3..1007bdf1 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -53,6 +53,7 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W filters.SeveritiesFilter, filters.PrioritiesFilter, filters.TagsFilter, + filters.WatchersFilter, filters.QFilter, filters.OrderByFilterMixin) retrieve_exclude_filters = (filters.OwnersFilter, @@ -61,11 +62,11 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W filters.IssueTypesFilter, filters.SeveritiesFilter, filters.PrioritiesFilter, - filters.TagsFilter,) + filters.TagsFilter, + filters.WatchersFilter,) filter_fields = ("project", - "status__is_closed", - "watchers") + "status__is_closed") order_by_fields = ("type", "status", "severity", @@ -142,7 +143,8 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W def get_queryset(self): qs = super().get_queryset() qs = qs.prefetch_related("attachments") - return self.attach_votes_attrs_to_queryset(qs) + qs = self.attach_votes_attrs_to_queryset(qs) + return self.attach_watchers_attrs_to_queryset(qs) def pre_save(self, obj): if not obj.id: diff --git a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py new file mode 100644 index 00000000..c41e387e --- /dev/null +++ b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM issues_issue_watchers""".format(content_type_id=ContentType.objects.get(model='issue').id)) + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ('issues', '0005_auto_20150623_1923'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='issue', + name='watchers', + ), + ] diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 423866bc..82120e14 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -35,6 +35,8 @@ class IssuePermission(TaigaResourcePermission): delete_comment_perms= HasProjectPerm('modify_issue') upvote_perms = IsAuthenticated() & HasProjectPerm('view_issues') downvote_perms = IsAuthenticated() & HasProjectPerm('view_issues') + watch_perms = IsAuthenticated() & HasProjectPerm('view_issues') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_issues') class HasIssueIdUrlParam(PermissionComponent): diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index 76023cf0..64c83f17 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -23,6 +23,7 @@ from taiga.mdrender.service import render as mdrender from taiga.projects.validators import ProjectExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicIssueStatusSerializer +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer @@ -30,7 +31,7 @@ from taiga.users.serializers import UserBasicInfoSerializer from . import models -class IssueSerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): +class IssueSerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(required=False) external_reference = PgArrayField(required=False) is_closed = serializers.Field(source="is_closed") diff --git a/taiga/projects/migrations/0024_auto_20150810_1247.py b/taiga/projects/migrations/0024_auto_20150810_1247.py new file mode 100644 index 00000000..ebe758fc --- /dev/null +++ b/taiga/projects/migrations/0024_auto_20150810_1247.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import djorm_pgarray.fields +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('projects', '0023_auto_20150721_1511'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='watchers', + field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, related_name='projects_project+', null=True, verbose_name='watchers'), + preserve_default=True, + ), + migrations.AlterField( + model_name='project', + name='public_permissions', + field=djorm_pgarray.fields.TextArrayField(default=[], dbtype='text', choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], verbose_name='user permissions'), + preserve_default=True, + ), + ] diff --git a/taiga/projects/migrations/0025_remove_project_watchers.py b/taiga/projects/migrations/0025_remove_project_watchers.py new file mode 100644 index 00000000..5748edc9 --- /dev/null +++ b/taiga/projects/migrations/0025_remove_project_watchers.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT project_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM projects_project_watchers""".format(content_type_id=ContentType.objects.get(model='project').id)) + + +class Migration(migrations.Migration): + dependencies = [ + ('notifications', '0004_watched'), + ('projects', '0024_auto_20150810_1247'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='project', + name='watchers', + ), + ] diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py index 132f9bf2..93a09831 100644 --- a/taiga/projects/milestones/api.py +++ b/taiga/projects/milestones/api.py @@ -44,9 +44,7 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView "user_stories__role_points__points", "user_stories__role_points__role", "user_stories__generated_from_issue", - "user_stories__project", - "watchers", - "user_stories__watchers") + "user_stories__project") qs = qs.select_related("project") qs = qs.order_by("-estimated_start") return qs diff --git a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py new file mode 100644 index 00000000..897f47bf --- /dev/null +++ b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM milestones_milestone_watchers""".format(content_type_id=ContentType.objects.get(model='milestone').id)), + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ('milestones', '0001_initial'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='milestone', + name='watchers', + ), + ] diff --git a/taiga/projects/milestones/serializers.py b/taiga/projects/milestones/serializers.py index 2ffd1d43..a2a483e5 100644 --- a/taiga/projects/milestones/serializers.py +++ b/taiga/projects/milestones/serializers.py @@ -19,12 +19,14 @@ from django.utils.translation import ugettext as _ from taiga.base.api import serializers from taiga.base.utils import json +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer +from taiga.projects.notifications.validators import WatchersValidator from ..userstories.serializers import UserStorySerializer from . import models -class MilestoneSerializer(serializers.ModelSerializer): +class MilestoneSerializer(WatchersValidator, WatchedResourceModelSerializer, serializers.ModelSerializer): user_stories = UserStorySerializer(many=True, required=False, read_only=True) total_points = serializers.SerializerMethodField("get_total_points") closed_points = serializers.SerializerMethodField("get_closed_points") diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 145edfb0..b2e2566f 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -40,6 +40,8 @@ from taiga.base.utils.slug import slugify_uniquely_for_queryset from . import choices +from . notifications.mixins import WatchedModelMixin + class Membership(models.Model): # This model stores all project memberships. Also @@ -118,7 +120,7 @@ class ProjectDefaults(models.Model): abstract = True -class Project(ProjectDefaults, TaggedMixin, models.Model): +class Project(ProjectDefaults, WatchedModelMixin, TaggedMixin, models.Model): name = models.CharField(max_length=250, null=False, blank=False, verbose_name=_("name")) slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, diff --git a/taiga/projects/notifications/migrations/0004_watched.py b/taiga/projects/notifications/migrations/0004_watched.py new file mode 100644 index 00000000..53c85560 --- /dev/null +++ b/taiga/projects/notifications/migrations/0004_watched.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +def fill_watched_table(apps, schema_editor): + Watched = apps.get_model("notifications", "Watched") + print("test") + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '0001_initial'), + ('notifications', '0003_auto_20141029_1143'), + ] + + operations = [ + migrations.CreateModel( + name='Watched', + fields=[ + ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), + ('object_id', models.PositiveIntegerField()), + ('created_date', models.DateTimeField(verbose_name='created date', auto_now_add=True)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('user', models.ForeignKey(related_name='watched', verbose_name='user', to=settings.AUTH_USER_MODEL)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.RunPython(fill_watched_table), + ] diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 362635a4..c5ffbb84 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -17,14 +17,22 @@ from functools import partial from operator import is_not -from django.conf import settings +from django.apps import apps +from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.translation import ugettext_lazy as _ +from taiga.base import response +from taiga.base.decorators import detail_route +from taiga.base.api import serializers +from taiga.base.fields import WatchersField from taiga.projects.notifications import services +from taiga.projects.notifications.utils import attach_watchers_to_queryset, attach_is_watched_to_queryset +from taiga.users.models import User +from . import models -class WatchedResourceMixin(object): +class WatchedResourceMixin: """ Rest Framework resource mixin for resources susceptible to be notifiable about their changes. @@ -36,6 +44,27 @@ class WatchedResourceMixin(object): _not_notify = False + def attach_watchers_attrs_to_queryset(self, queryset): + qs = attach_watchers_to_queryset(queryset) + if self.request.user.is_authenticated(): + qs = attach_is_watched_to_queryset(self.request.user, qs) + + return qs + + @detail_route(methods=["POST"]) + def watch(self, request, pk=None): + obj = self.get_object() + self.check_permissions(request, "watch", obj) + services.add_watcher(obj, request.user) + return response.Ok() + + @detail_route(methods=["POST"]) + def unwatch(self, request, pk=None): + obj = self.get_object() + self.check_permissions(request, "unwatch", obj) + services.remove_watcher(obj, request.user) + return response.Ok() + def send_notifications(self, obj, history=None): """ Shortcut method for resources with special save @@ -73,7 +102,7 @@ class WatchedResourceMixin(object): super().pre_delete(obj) -class WatchedModelMixin(models.Model): +class WatchedModelMixin(object): """ Generic model mixin that makes model compatible with notification system. @@ -82,11 +111,6 @@ class WatchedModelMixin(models.Model): this mixin if you want send notifications about your model class. """ - watchers = models.ManyToManyField(settings.AUTH_USER_MODEL, null=True, blank=True, - related_name="%(app_label)s_%(class)s+", - verbose_name=_("watchers")) - class Meta: - abstract = True def get_project(self) -> object: """ @@ -97,6 +121,7 @@ class WatchedModelMixin(models.Model): that should works in almost all cases. """ return self.project + t def get_watchers(self) -> frozenset: """ @@ -112,7 +137,13 @@ class WatchedModelMixin(models.Model): very inefficient way for obtain watchers but at this momment is the simplest way. """ - return frozenset(self.watchers.all()) + return frozenset(services.get_watchers(self)) + + def add_watcher(self, user): + services.add_watcher(self, user) + + def remove_watcher(self, user): + services.remove_watcher(self, user) def get_owner(self) -> object: """ @@ -140,3 +171,79 @@ class WatchedModelMixin(models.Model): self.get_owner(),) is_not_none = partial(is_not, None) return frozenset(filter(is_not_none, participants)) + + +class WatchedResourceModelSerializer(serializers.ModelSerializer): + is_watched = serializers.SerializerMethodField("get_is_watched") + watchers = WatchersField(required=False) + + def get_is_watched(self, obj): + # The "is_watched" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "is_watched", False) or False + + def restore_object(self, attrs, instance=None): + #watchers is not a field from the model but can be attached in the get_queryset of the viewset. + #If that's the case we need to remove it before calling the super method + watcher_field = self.fields.pop("watchers", None) + instance = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) + if instance is not None and self.validate_watchers(attrs, "watchers"): + new_watcher_ids = set(attrs.get("watchers", [])) + old_watcher_ids = set(services.get_watchers(instance).values_list("id", flat=True)) + adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) + removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) + + User = apps.get_model("users", "User") + adding_users = User.objects.filter(id__in=adding_watcher_ids) + removing_users = User.objects.filter(id__in=removing_watcher_ids) + for user in adding_users: + services.add_watcher(instance, user) + + for user in removing_users: + services.remove_watcher(instance, user) + + instance.watchers = services.get_watchers(instance) + + return instance + + + def to_native(self, obj): + #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it + if not hasattr(obj, "watchers"): + obj.watchers = services.get_watchers(obj) + + return super(WatchedResourceModelSerializer, self).to_native(obj) + + +class WatchersViewSetMixin: + # Is a ModelListViewSet with two required params: permission_classes and resource_model + serializer_class = WatcherSerializer + list_serializer_class = WatcherSerializer + permission_classes = None + resource_model = None + + def retrieve(self, request, *args, **kwargs): + pk = kwargs.get("pk", None) + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'retrieve', resource) + + try: + self.object = services.get_watchers(resource).get(pk=pk) + except ObjectDoesNotExist: # or User.DoesNotExist + return response.NotFound() + + serializer = self.get_serializer(self.object) + return response.Ok(serializer.data) + + def list(self, request, *args, **kwargs): + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'list', resource) + + return super().list(request, *args, **kwargs) + + def get_queryset(self): + resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) + return services.get_watchers(resource) diff --git a/taiga/projects/notifications/models.py b/taiga/projects/notifications/models.py index 29983f90..753b8878 100644 --- a/taiga/projects/notifications/models.py +++ b/taiga/projects/notifications/models.py @@ -14,6 +14,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 django.contrib.contenttypes import generic from django.db import models from django.utils.translation import ugettext_lazy as _ from django.utils import timezone @@ -72,3 +74,18 @@ class HistoryChangeNotification(models.Model): class Meta: unique_together = ("key", "owner", "project", "history_type") + + +class Watched(models.Model): + content_type = models.ForeignKey("contenttypes.ContentType") + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey("content_type", "object_id") + user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False, + related_name="watched", verbose_name=_("user")) + created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, + verbose_name=_("created date")) + + class Meta: + verbose_name = _("Watched") + verbose_name_plural = _("Watched") + unique_together = ("content_type", "object_id", "user") diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 75f7e48b..be19a493 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -17,10 +17,10 @@ from functools import partial from django.apps import apps -from django.db import IntegrityError +from django.db.transaction import atomic +from django.db import IntegrityError, transaction from django.contrib.contenttypes.models import ContentType from django.utils import timezone -from django.db import transaction from django.conf import settings from django.utils.translation import ugettext as _ @@ -36,7 +36,7 @@ from taiga.projects.history.services import (make_key_from_model_object, from taiga.permissions.service import user_has_perm from taiga.users.models import User -from .models import HistoryChangeNotification +from .models import HistoryChangeNotification, Watched def notify_policy_exists(project, user) -> bool: @@ -121,11 +121,11 @@ def analize_object_for_watchers(obj:object, history:object): if data["mentions"]: for user in data["mentions"]: - obj.watchers.add(user) + obj.add_watcher(user) # Adding the person who edited the object to the watchers if history.comment and not history.owner.is_system: - obj.watchers.add(history.owner) + obj.add_watcher(history.owner) def _filter_by_permissions(obj, user): UserStory = apps.get_model("userstories", "UserStory") @@ -282,3 +282,46 @@ def send_sync_notifications(notification_id): def process_sync_notifications(): for notification in HistoryChangeNotification.objects.all(): send_sync_notifications(notification.pk) + + +def get_watchers(obj): + User = apps.get_model("users", "User") + Watched = apps.get_model("notifications", "Watched") + content_type = ContentType.objects.get_for_model(obj) + watching_user_ids = Watched.objects.filter(content_type=content_type, object_id=obj.id).values_list("user__id", flat=True) + return User.objects.filter(id__in=watching_user_ids) + + +def add_watcher(obj, user): + """Add a watcher to an object. + + If the user is already watching the object nothing happends, so this function can be considered + idempotent. + + :param obj: Any Django model instance. + :param user: User adding the watch. :class:`~taiga.users.models.User` instance. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + with atomic(): + watched, created = Watched.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) + if not created: + return + return watched + + +def remove_watcher(obj, user): + """Remove an watching user from an object. + + If the user has not watched the object nothing happens so this function can be considered + idempotent. + + :param obj: Any Django model instance. + :param user: User removing the watch. :class:`~taiga.users.models.User` instance. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + with atomic(): + qs = Watched.objects.filter(content_type=obj_type, object_id=obj.id, user=user) + if not qs.exists(): + return + + qs.delete() diff --git a/taiga/projects/notifications/utils.py b/taiga/projects/notifications/utils.py new file mode 100644 index 00000000..14edd0b3 --- /dev/null +++ b/taiga/projects/notifications/utils.py @@ -0,0 +1,63 @@ +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino +# Copyright (C) 2014 David Barragán +# Copyright (C) 2014 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.apps import apps + + +def attach_watchers_to_queryset(queryset, as_field="watchers"): + """Attach watching user ids to each object of the queryset. + + :param queryset: A Django queryset object. + :param as_field: Attach the watchers as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + + sql = ("""SELECT array(SELECT user_id + FROM notifications_watched + WHERE notifications_watched.content_type_id = {type_id} + AND notifications_watched.object_id = {tbl}.id)""") + sql = sql.format(type_id=type.id, tbl=model._meta.db_table) + qs = queryset.extra(select={as_field: sql}) + + return qs + + +def attach_is_watched_to_queryset(user, queryset, as_field="is_watched"): + """Attach is_watched boolean to each object of the queryset. + + :param user: A users.User object model + :param queryset: A Django queryset object. + :param as_field: Attach the boolean as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = ("""SELECT CASE WHEN (SELECT count(*) + FROM notifications_watched + WHERE notifications_watched.content_type_id = {type_id} + AND notifications_watched.object_id = {tbl}.id + AND notifications_watched.user_id = {user_id}) > 0 + THEN TRUE + ELSE FALSE + END""") + sql = sql.format(type_id=type.id, tbl=model._meta.db_table, user_id=user.id) + qs = queryset.extra(select={as_field: sql}) + return qs diff --git a/taiga/projects/notifications/validators.py b/taiga/projects/notifications/validators.py index 38c5750b..b28e0712 100644 --- a/taiga/projects/notifications/validators.py +++ b/taiga/projects/notifications/validators.py @@ -21,7 +21,7 @@ from taiga.base.api import serializers class WatchersValidator: def validate_watchers(self, attrs, source): - users = attrs[source] + users = attrs.get(source, []) # Try obtain a valid project if self.object is None and "project" in attrs: @@ -39,7 +39,8 @@ class WatchersValidator: # Check if incoming watchers are contained # in project members list - result = set(users).difference(set(project.members.all())) + member_ids = project.members.values_list("id", flat=True) + result = set(users).difference(member_ids) if result: raise serializers.ValidationError(_("Watchers contains invalid users")) diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 236bc182..9cabdc97 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -62,6 +62,8 @@ class ProjectPermission(TaigaResourcePermission): tags_colors_perms = HasProjectPerm('view_project') star_perms = IsAuthenticated() & HasProjectPerm('view_project') unstar_perms = IsAuthenticated() & HasProjectPerm('view_project') + watch_perms = IsAuthenticated() & HasProjectPerm('view_project') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_project') create_template_perms = IsSuperUser() leave_perms = CanLeaveProject() diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index a7e96834..9a13c6e7 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -25,6 +25,8 @@ from taiga.base.fields import PgArrayField from taiga.base.fields import TagsField from taiga.base.fields import TagsColorsField +from taiga.projects.notifications.validators import WatchersValidator + from taiga.users.services import get_photo_or_gravatar_url from taiga.users.serializers import UserSerializer from taiga.users.serializers import UserBasicInfoSerializer @@ -40,6 +42,7 @@ from .validators import ProjectExistsValidator from .custom_attributes.serializers import UserStoryCustomAttributeSerializer from .custom_attributes.serializers import TaskCustomAttributeSerializer from .custom_attributes.serializers import IssueCustomAttributeSerializer +from .notifications.mixins import WatchedResourceModelSerializer from .votes.mixins.serializers import StarredResourceSerializerMixin ###################################################### @@ -305,7 +308,7 @@ class ProjectMemberSerializer(serializers.ModelSerializer): ## Projects ###################################################### -class ProjectSerializer(StarredResourceSerializerMixin, serializers.ModelSerializer): +class ProjectSerializer(WatchersValidator, StarredResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) anon_permissions = PgArrayField(required=False) public_permissions = PgArrayField(required=False) diff --git a/taiga/projects/signals.py b/taiga/projects/signals.py index f6cb0b1a..6cfb0ddf 100644 --- a/taiga/projects/signals.py +++ b/taiga/projects/signals.py @@ -45,24 +45,6 @@ def membership_post_delete(sender, instance, using, **kwargs): instance.project.update_role_points() -def update_watchers_on_membership_post_delete(sender, instance, using, **kwargs): - models = [apps.get_model("userstories", "UserStory"), - apps.get_model("tasks", "Task"), - apps.get_model("issues", "Issue")] - - # `user_id` is used beacuse in some momments - # instance.user can contain pointer to now - # removed object from a database. - for model in models: - #filter(project=instance.project) - filter = { - "user_id": instance.user_id, - "%s__project"%(model._meta.model_name): instance.project, - } - - model.watchers.through.objects.filter(**filter).delete() - - def create_notify_policy(sender, instance, using, **kwargs): if instance.user: create_notify_policy_if_not_exists(instance.project, instance.user) diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index e2262632..2e2ad619 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -40,9 +40,10 @@ class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, Wa ModelCrudViewSet): queryset = models.Task.objects.all() permission_classes = (permissions.TaskPermission,) - filter_backends = (filters.CanViewTasksFilterBackend,) + filter_backends = (filters.CanViewTasksFilterBackend, filters.WatchersFilter) + retrieve_exclude_filters = (filters.WatchersFilter,) filter_fields = ["user_story", "milestone", "project", "assigned_to", - "status__is_closed", "watchers"] + "status__is_closed"] def get_serializer_class(self, *args, **kwargs): if self.action in ["retrieve", "by_ref"]: @@ -86,7 +87,8 @@ class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, Wa def get_queryset(self): qs = super().get_queryset() - return self.attach_votes_attrs_to_queryset(qs) + qs = self.attach_votes_attrs_to_queryset(qs) + return self.attach_watchers_attrs_to_queryset(qs) def pre_save(self, obj): if obj.user_story: diff --git a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py new file mode 100644 index 00000000..813eaad9 --- /dev/null +++ b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM tasks_task_watchers""".format(content_type_id=ContentType.objects.get(model='task').id)), + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ('tasks', '0007_auto_20150629_1556'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='task', + name='watchers', + ), + ] diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index af5fbd49..c9cbd667 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -33,6 +33,8 @@ class TaskPermission(TaigaResourcePermission): bulk_update_order_perms = HasProjectPerm('modify_task') upvote_perms = IsAuthenticated() & HasProjectPerm('view_tasks') downvote_perms = IsAuthenticated() & HasProjectPerm('view_tasks') + watch_perms = IsAuthenticated() & HasProjectPerm('view_tasks') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_tasks') class TaskVotersPermission(TaigaResourcePermission): diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 3df25a77..221a188c 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -27,6 +27,7 @@ from taiga.projects.milestones.validators import SprintExistsValidator from taiga.projects.tasks.validators import TaskExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicTaskStatusSerializerSerializer +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer @@ -34,7 +35,7 @@ from taiga.users.serializers import UserBasicInfoSerializer from . import models -class TaskSerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): +class TaskSerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(required=False, default=[]) external_reference = PgArrayField(required=False) comment = serializers.SerializerMethodField("get_comment") diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 8ae11d53..653fdc56 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -53,19 +53,20 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi filters.AssignedToFilter, filters.StatusesFilter, filters.TagsFilter, + filters.WatchersFilter, filters.QFilter, filters.OrderByFilterMixin) retrieve_exclude_filters = (filters.OwnersFilter, filters.AssignedToFilter, filters.StatusesFilter, - filters.TagsFilter) + filters.TagsFilter, + filters.WatchersFilter) filter_fields = ["project", "milestone", "milestone__isnull", "is_closed", "status__is_archived", - "status__is_closed", - "watchers"] + "status__is_closed"] order_by_fields = ["backlog_order", "sprint_order", "kanban_order"] @@ -113,10 +114,10 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi qs = super().get_queryset() qs = qs.prefetch_related("role_points", "role_points__points", - "role_points__role", - "watchers") + "role_points__role") qs = qs.select_related("milestone", "project") - return self.attach_votes_attrs_to_queryset(qs) + qs = self.attach_votes_attrs_to_queryset(qs) + return self.attach_watchers_attrs_to_queryset(qs) def pre_save(self, obj): # This is very ugly hack, but having diff --git a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py new file mode 100644 index 00000000..d3e24f62 --- /dev/null +++ b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM userstories_userstory_watchers""".format(content_type_id=ContentType.objects.get(model='userstory').id)), + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ('userstories', '0009_remove_userstory_is_archived'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='userstory', + name='watchers', + ), + ] diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 00659141..0a1c7b8a 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -32,7 +32,8 @@ class UserStoryPermission(TaigaResourcePermission): bulk_update_order_perms = HasProjectPerm('modify_us') upvote_perms = IsAuthenticated() & HasProjectPerm('view_us') downvote_perms = IsAuthenticated() & HasProjectPerm('view_us') - + watch_perms = IsAuthenticated() & HasProjectPerm('view_us') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_us') class UserStoryVotersPermission(TaigaResourcePermission): enought_perms = IsProjectOwner() | IsSuperUser() diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 6c3a76cd..a461e57b 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -27,6 +27,7 @@ from taiga.projects.validators import UserStoryStatusExistsValidator from taiga.projects.userstories.validators import UserStoryExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicUserStoryStatusSerializer +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer @@ -44,7 +45,7 @@ class RolePointsField(serializers.WritableField): return json.loads(obj) -class UserStorySerializer(WatchersValidator, VotedResourceSerializerMixin, serializers.ModelSerializer): +class UserStorySerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) external_reference = PgArrayField(required=False) points = RolePointsField(source="role_points", required=False) diff --git a/taiga/projects/votes/serializers.py b/taiga/projects/votes/serializers.py index c72ae91e..b6ab72a8 100644 --- a/taiga/projects/votes/serializers.py +++ b/taiga/projects/votes/serializers.py @@ -16,8 +16,10 @@ # along with this program. If not, see . from taiga.base.api import serializers +from taiga.base.fields import TagsField from taiga.users.models import User +from taiga.users.services import get_photo_or_gravatar_url class VoterSerializer(serializers.ModelSerializer): diff --git a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py new file mode 100644 index 00000000..d0c1c832 --- /dev/null +++ b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.apps import apps +from django.db import models, migrations +from django.contrib.contenttypes.models import ContentType +from django.contrib.contenttypes.management import update_all_contenttypes + +def create_notifications(apps, schema_editor): + update_all_contenttypes() + migrations.RunSQL(sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) +SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id +FROM wiki_wikipage_watchers""".format(content_type_id=ContentType.objects.get(model='wikipage').id)), + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ('wiki', '0001_initial'), + ] + + operations = [ + migrations.RunPython(create_notifications), + migrations.RemoveField( + model_name='wikipage', + name='watchers', + ), + ] diff --git a/taiga/projects/wiki/serializers.py b/taiga/projects/wiki/serializers.py index 45d3c99b..a528fdd8 100644 --- a/taiga/projects/wiki/serializers.py +++ b/taiga/projects/wiki/serializers.py @@ -15,6 +15,10 @@ # along with this program. If not, see . from taiga.base.api import serializers +from taiga.projects.history import services as history_service +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer +from taiga.projects.notifications.validators import WatchersValidator +from taiga.mdrender.service import render as mdrender from . import models @@ -23,7 +27,7 @@ from taiga.projects.history import services as history_service from taiga.mdrender.service import render as mdrender -class WikiPageSerializer(serializers.ModelSerializer): +class WikiPageSerializer(WatchersValidator, WatchedResourceModelSerializer, serializers.ModelSerializer): html = serializers.SerializerMethodField("get_html") editions = serializers.SerializerMethodField("get_editions") @@ -39,6 +43,5 @@ class WikiPageSerializer(serializers.ModelSerializer): class WikiLinkSerializer(serializers.ModelSerializer): - class Meta: model = models.WikiLink diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 7769817d..d56db4ac 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -62,7 +62,7 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d ## - Watchers watchers = getattr(obj, "watchers", None) if watchers: - related_people |= obj.watchers.all() + related_people |= obj.get_watchers() ## - Exclude inactive and system users and remove duplicate related_people = related_people.exclude(is_active=False) diff --git a/taiga/users/migrations/0012_auto_20150812_1142.py b/taiga/users/migrations/0012_auto_20150812_1142.py new file mode 100644 index 00000000..fff8c17b --- /dev/null +++ b/taiga/users/migrations/0012_auto_20150812_1142.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import djorm_pgarray.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0011_user_theme'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='permissions', + field=djorm_pgarray.fields.TextArrayField(choices=[('view_project', 'View project'), ('star_project', 'Star project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('vote_us', 'Vote user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('vote_task', 'Vote task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('vote_issue', 'Vote issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], verbose_name='permissions', default=[], dbtype='text'), + preserve_default=True, + ), + ] diff --git a/taiga/webhooks/serializers.py b/taiga/webhooks/serializers.py index e802c1b3..47d0a145 100644 --- a/taiga/webhooks/serializers.py +++ b/taiga/webhooks/serializers.py @@ -23,8 +23,9 @@ from taiga.projects.userstories import models as us_models from taiga.projects.tasks import models as task_models from taiga.projects.issues import models as issue_models from taiga.projects.milestones import models as milestone_models -from taiga.projects.history import models as history_models from taiga.projects.wiki import models as wiki_models +from taiga.projects.history import models as history_models +from taiga.projects.notifications.mixins import WatchedResourceModelSerializer from .models import Webhook, WebhookLog @@ -103,7 +104,8 @@ class PointSerializer(serializers.Serializer): return obj.value -class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, serializers.ModelSerializer): +class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, + serializers.ModelSerializer): tags = TagsField(default=[], required=False) external_reference = PgArrayField(required=False) owner = UserSerializer() @@ -119,7 +121,8 @@ class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, serializ return project.userstorycustomattributes.all() -class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, serializers.ModelSerializer): +class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, + serializers.ModelSerializer): tags = TagsField(default=[], required=False) owner = UserSerializer() assigned_to = UserSerializer() @@ -132,7 +135,8 @@ class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, serializers.M return project.taskcustomattributes.all() -class IssueSerializer(CustomAttributesValuesWebhookSerializerMixin, serializers.ModelSerializer): +class IssueSerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, + serializers.ModelSerializer): tags = TagsField(default=[], required=False) owner = UserSerializer() assigned_to = UserSerializer() diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index f5a90e66..d9950e00 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -574,3 +574,45 @@ def test_issues_csv(client, data): results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) assert results == [200, 200, 200, 200, 200] + + +def test_issue_action_watch(client, data): + public_url = reverse('issues-watch', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-watch', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-watch', kwargs={"pk": data.private_issue2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_issue_action_unwatch(client, data): + public_url = reverse('issues-unwatch', kwargs={"pk": data.public_issue.pk}) + private_url1 = reverse('issues-unwatch', kwargs={"pk": data.private_issue1.pk}) + private_url2 = reverse('issues-unwatch', kwargs={"pk": data.private_issue2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index e485f497..888b1ef4 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -416,3 +416,41 @@ def test_regenerate_issues_csv_uuid(client, data): results = helper_test_http_method(client, 'post', private2_url, None, users) assert results == [404, 404, 403, 200] + + +def test_project_action_watch(client, data): + public_url = reverse('projects-watch', kwargs={"pk": data.public_project.pk}) + private1_url = reverse('projects-watch', kwargs={"pk": data.private_project1.pk}) + private2_url = reverse('projects-watch', kwargs={"pk": data.private_project2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_with_perms, + data.project_owner + ] + results = helper_test_http_method(client, 'post', public_url, None, users) + assert results == [401, 200, 200, 200] + results = helper_test_http_method(client, 'post', private1_url, None, users) + assert results == [401, 200, 200, 200] + results = helper_test_http_method(client, 'post', private2_url, None, users) + assert results == [404, 404, 200, 200] + + +def test_project_action_unwatch(client, data): + public_url = reverse('projects-unwatch', kwargs={"pk": data.public_project.pk}) + private1_url = reverse('projects-unwatch', kwargs={"pk": data.private_project1.pk}) + private2_url = reverse('projects-unwatch', kwargs={"pk": data.private_project2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_with_perms, + data.project_owner + ] + results = helper_test_http_method(client, 'post', public_url, None, users) + assert results == [401, 200, 200, 200] + results = helper_test_http_method(client, 'post', private1_url, None, users) + assert results == [401, 200, 200, 200] + results = helper_test_http_method(client, 'post', private2_url, None, users) + assert results == [404, 404, 200, 200] diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index b5c3c4f0..bd4d0e09 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -529,3 +529,45 @@ def test_tasks_csv(client, data): results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) assert results == [200, 200, 200, 200, 200] + + +def test_task_action_watch(client, data): + public_url = reverse('tasks-watch', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-watch', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-watch', kwargs={"pk": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_task_action_unwatch(client, data): + public_url = reverse('tasks-unwatch', kwargs={"pk": data.public_task.pk}) + private_url1 = reverse('tasks-unwatch', kwargs={"pk": data.private_task1.pk}) + private_url2 = reverse('tasks-unwatch', kwargs={"pk": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index c257244b..219c4e2a 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -528,3 +528,45 @@ def test_user_stories_csv(client, data): results = helper_test_http_method(client, 'get', "{}?uuid={}".format(url, csv_private2_uuid), None, users) assert results == [200, 200, 200, 200, 200] + + +def test_user_story_action_watch(client, data): + public_url = reverse('userstories-watch', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-watch', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-watch', kwargs={"pk": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_user_story_action_unwatch(client, data): + public_url = reverse('userstories-unwatch', kwargs={"pk": data.public_user_story.pk}) + private_url1 = reverse('userstories-unwatch', kwargs={"pk": data.private_user_story1.pk}) + private_url2 = reverse('userstories-unwatch', kwargs={"pk": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index 96143112..073d7585 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -47,13 +47,15 @@ def test_invalid_project_import(client): def test_valid_project_import_without_extra_data(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") client.login(user) url = reverse("importer-list") data = { "name": "Imported project", "description": "Imported project", - "roles": [{"name": "Role"}] + "roles": [{"name": "Role"}], + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") @@ -66,6 +68,7 @@ def test_valid_project_import_without_extra_data(client): ] assert all(map(lambda x: len(response_data[x]) == 0, must_empty_children)) assert response_data["owner"] == user.email + assert response_data["watchers"] == [user_watching.email] def test_valid_project_import_with_not_existing_memberships(client): @@ -383,6 +386,7 @@ def test_valid_issue_import_with_custom_attributes_values(client): def test_valid_issue_import_with_extra_data(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") project = f.ProjectFactory.create(owner=user) f.MembershipFactory(project=project, user=user, is_owner=True) project.default_issue_type = f.IssueTypeFactory.create(project=project) @@ -403,7 +407,8 @@ def test_valid_issue_import_with_extra_data(client): "name": "imported attachment", "data": base64.b64encode(b"TEST").decode("utf-8") } - }] + }], + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") @@ -413,6 +418,7 @@ def test_valid_issue_import_with_extra_data(client): assert response_data["owner"] == user.email assert response_data["ref"] is not None assert response_data["finished_date"] == "2014-10-24T00:00:00+0000" + assert response_data["watchers"] == [user_watching.email] def test_invalid_issue_import_with_extra_data(client): @@ -535,6 +541,7 @@ def test_valid_us_import_without_extra_data(client): def test_valid_us_import_with_extra_data(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") project = f.ProjectFactory.create(owner=user) f.MembershipFactory(project=project, user=user, is_owner=True) project.default_us_status = f.UserStoryStatusFactory.create(project=project) @@ -551,7 +558,8 @@ def test_valid_us_import_with_extra_data(client): "name": "imported attachment", "data": base64.b64encode(b"TEST").decode("utf-8") } - }] + }], + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") @@ -560,6 +568,7 @@ def test_valid_us_import_with_extra_data(client): assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email assert response_data["ref"] is not None + assert response_data["watchers"] == [user_watching.email] def test_invalid_us_import_with_extra_data(client): @@ -664,6 +673,7 @@ def test_valid_task_import_with_custom_attributes_values(client): def test_valid_task_import_with_extra_data(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") project = f.ProjectFactory.create(owner=user) f.MembershipFactory(project=project, user=user, is_owner=True) project.default_task_status = f.TaskStatusFactory.create(project=project) @@ -680,7 +690,8 @@ def test_valid_task_import_with_extra_data(client): "name": "imported attachment", "data": base64.b64encode(b"TEST").decode("utf-8") } - }] + }], + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") @@ -689,6 +700,7 @@ def test_valid_task_import_with_extra_data(client): assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email assert response_data["ref"] is not None + assert response_data["watchers"] == [user_watching.email] def test_invalid_task_import_with_extra_data(client): @@ -787,6 +799,7 @@ def test_valid_wiki_page_import_without_extra_data(client): def test_valid_wiki_page_import_with_extra_data(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") project = f.ProjectFactory.create(owner=user) f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) @@ -801,7 +814,8 @@ def test_valid_wiki_page_import_with_extra_data(client): "name": "imported attachment", "data": base64.b64encode(b"TEST").decode("utf-8") } - }] + }], + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") @@ -809,6 +823,7 @@ def test_valid_wiki_page_import_with_extra_data(client): response_data = response.data assert len(response_data["attachments"]) == 1 assert response_data["owner"] == user.email + assert response_data["watchers"] == [user_watching.email] def test_invalid_wiki_page_import_with_extra_data(client): @@ -877,6 +892,7 @@ def test_invalid_milestone_import(client): def test_valid_milestone_import(client): user = f.UserFactory.create() + user_watching = f.UserFactory.create(email="testing@taiga.io") project = f.ProjectFactory.create(owner=user) f.MembershipFactory(project=project, user=user, is_owner=True) client.login(user) @@ -886,11 +902,12 @@ def test_valid_milestone_import(client): "name": "Imported milestone", "estimated_start": "2014-10-10", "estimated_finish": "2014-10-20", + "watchers": ["testing@taiga.io"] } response = client.post(url, json.dumps(data), content_type="application/json") assert response.status_code == 201 - response.data + assert response.data["watchers"] == [user_watching.email] diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index a5a9847c..ab0fdb42 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -97,7 +97,7 @@ def test_analize_object_for_watchers(): history.comment = "" services.analize_object_for_watchers(issue, history) - assert issue.watchers.add.call_count == 2 + assert issue.add_watcher.call_count == 2 def test_analize_object_for_watchers_adding_owner_non_empty_comment(): @@ -112,7 +112,7 @@ def test_analize_object_for_watchers_adding_owner_non_empty_comment(): history.owner = user1 services.analize_object_for_watchers(issue, history) - assert issue.watchers.add.call_count == 1 + assert issue.add_watcher.call_count == 1 def test_analize_object_for_watchers_no_adding_owner_empty_comment(): @@ -127,7 +127,7 @@ def test_analize_object_for_watchers_no_adding_owner_empty_comment(): history.owner = user1 services.analize_object_for_watchers(issue, history) - assert issue.watchers.add.call_count == 0 + assert issue.add_watcher.call_count == 0 def test_users_to_notify(): @@ -180,7 +180,7 @@ def test_users_to_notify(): assert users == {member1.user, issue.get_owner()} # Test with watchers - issue.watchers.add(member3.user) + issue.add_watcher(member3.user) users = services.get_users_to_notify(issue) assert len(users) == 3 assert users == {member1.user, member3.user, issue.get_owner()} @@ -189,24 +189,24 @@ def test_users_to_notify(): policy2.notify_level = NotifyLevel.ignore policy2.save() - issue.watchers.add(member3.user) + issue.add_watcher(member3.user) users = services.get_users_to_notify(issue) assert len(users) == 2 assert users == {member1.user, issue.get_owner()} # Test with watchers without permissions - issue.watchers.add(member5.user) + issue.add_watcher(member5.user) users = services.get_users_to_notify(issue) assert len(users) == 2 assert users == {member1.user, issue.get_owner()} # Test with inactive user - issue.watchers.add(inactive_member1.user) + issue.add_watcher(inactive_member1.user) assert len(users) == 2 assert users == {member1.user, issue.get_owner()} # Test with system user - issue.watchers.add(system_member1.user) + issue.add_watcher(system_member1.user) assert len(users) == 2 assert users == {member1.user, issue.get_owner()} @@ -344,7 +344,7 @@ def test_watchers_assignation_for_issue(client): issue = f.create_issue(project=project1, owner=user1) data = {"version": issue.version, - "watchers": [user1.pk]} + "watchersa": [user1.pk]} url = reverse("issues-detail", args=[issue.pk]) response = client.json.patch(url, json.dumps(data)) diff --git a/tests/integration/test_projects.py b/tests/integration/test_projects.py index 45b7c96e..2a4f8e5c 100644 --- a/tests/integration/test_projects.py +++ b/tests/integration/test_projects.py @@ -265,7 +265,7 @@ def test_leave_project_respect_watching_items(client): url = reverse("projects-leave", args=(project.id,)) response = client.post(url) assert response.status_code == 200 - assert list(issue.watchers.all()) == [user] + assert issue.watchers == [user] def test_delete_membership_only_owner(client): diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 957c5c91..00e61fc3 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -384,16 +384,6 @@ def test_assigned_to_user_story_timeline(): assert user_timeline[0].data["userstory"]["subject"] == "test us timeline" -def test_watchers_to_user_story_timeline(): - membership = factories.MembershipFactory.create() - user_story = factories.UserStoryFactory.create(subject="test us timeline", project=membership.project) - user_story.watchers.add(membership.user) - history_services.take_snapshot(user_story, user=user_story.owner) - user_timeline = service.get_profile_timeline(membership.user) - assert user_timeline[0].event_type == "userstories.userstory.create" - assert user_timeline[0].data["userstory"]["subject"] == "test us timeline" - - def test_user_data_for_non_system_users(): user_story = factories.UserStoryFactory.create(subject="test us timeline") history_services.take_snapshot(user_story, user=user_story.owner) diff --git a/tests/integration/test_watch_issues.py b/tests/integration/test_watch_issues.py new file mode 100644 index 00000000..f51d5075 --- /dev/null +++ b/tests/integration/test_watch_issues.py @@ -0,0 +1,47 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_issue(client): + user = f.UserFactory.create() + issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + url = reverse("issues-watch", args=(issue.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwatch_issue(client): + user = f.UserFactory.create() + issue = f.create_issue(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + url = reverse("issues-watch", args=(issue.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 diff --git a/tests/integration/test_watch_milestones.py b/tests/integration/test_watch_milestones.py new file mode 100644 index 00000000..72fea24d --- /dev/null +++ b/tests/integration/test_watch_milestones.py @@ -0,0 +1,123 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +import json +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_milestone(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + url = reverse("milestones-watch", args=(milestone.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwatch_milestone(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + url = reverse("milestones-watch", args=(milestone.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_milestone_watchers(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=milestone, user=user) + url = reverse("milestone-watchers-list", args=(milestone.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_milestone_watcher(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=milestone, user=user) + url = reverse("milestone-watchers-detail", args=(milestone.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_milestone_watchers(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + url = reverse("milestones-detail", args=(milestone.id,)) + + f.WatchedFactory.create(content_object=milestone, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_milestone_is_watched(client): + user = f.UserFactory.create() + milestone = f.MilestoneFactory(owner=user) + f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) + url_detail = reverse("milestones-detail", args=(milestone.id,)) + url_watch = reverse("milestones-watch", args=(milestone.id,)) + url_unwatch = reverse("milestones-unwatch", args=(milestone.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py new file mode 100644 index 00000000..8bb765ce --- /dev/null +++ b/tests/integration/test_watch_projects.py @@ -0,0 +1,47 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-watch", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwacth_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-unwatch", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 diff --git a/tests/integration/test_watch_tasks.py b/tests/integration/test_watch_tasks.py new file mode 100644 index 00000000..f62e4c7b --- /dev/null +++ b/tests/integration/test_watch_tasks.py @@ -0,0 +1,47 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_task(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-watch", args=(task.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwatch_task(client): + user = f.UserFactory.create() + task = f.create_task(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-watch", args=(task.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 diff --git a/tests/integration/test_watch_userstories.py b/tests/integration/test_watch_userstories.py new file mode 100644 index 00000000..a6a7123e --- /dev/null +++ b/tests/integration/test_watch_userstories.py @@ -0,0 +1,47 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_user_story(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-watch", args=(user_story.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwatch_user_story(client): + user = f.UserFactory.create() + user_story = f.create_userstory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-unwatch", args=(user_story.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 diff --git a/tests/integration/test_watch_wikipages.py b/tests/integration/test_watch_wikipages.py new file mode 100644 index 00000000..c4f96bb6 --- /dev/null +++ b/tests/integration/test_watch_wikipages.py @@ -0,0 +1,123 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +import json +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_watch_wikipage(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + url = reverse("wiki-watch", args=(wikipage.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unwatch_wikipage(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + url = reverse("wiki-watch", args=(wikipage.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_wikipage_watchers(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=wikipage, user=user) + url = reverse("wiki-watchers-list", args=(wikipage.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_wikipage_watcher(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=wikipage, user=user) + url = reverse("wiki-watchers-detail", args=(wikipage.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_wikipage_watchers(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + url = reverse("wiki-detail", args=(wikipage.id,)) + + f.WatchedFactory.create(content_object=wikipage, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_wikipage_is_watched(client): + user = f.UserFactory.create() + wikipage = f.WikiPageFactory(owner=user) + f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) + url_detail = reverse("wiki-detail", args=(wikipage.id,)) + url_watch = reverse("wiki-watch", args=(wikipage.id,)) + url_unwatch = reverse("wiki-unwatch", args=(wikipage.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False From bccdc2fae147740ddd006276ddbbd198618be396 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 18 Aug 2015 09:23:10 +0200 Subject: [PATCH 102/190] Adding votes and watched projects to sample_data --- taiga/projects/api.py | 6 +- taiga/projects/issues/api.py | 7 +- .../migrations/0006_remove_issue_watchers.py | 14 ++- taiga/projects/issues/permissions.py | 7 ++ taiga/projects/issues/services.py | 7 +- .../management/commands/sample_data.py | 24 +++- .../migrations/0024_auto_20150810_1247.py | 6 - .../0025_remove_project_watchers.py | 28 ----- taiga/projects/milestones/api.py | 13 +- .../0002_remove_milestone_watchers.py | 13 +- taiga/projects/milestones/permissions.py | 12 +- taiga/projects/milestones/serializers.py | 1 - taiga/projects/notifications/api.py | 22 +++- .../notifications/migrations/0004_watched.py | 15 ++- taiga/projects/notifications/mixins.py | 10 +- taiga/projects/notifications/models.py | 7 +- taiga/projects/notifications/serializers.py | 11 +- taiga/projects/notifications/services.py | 67 +++++++--- taiga/projects/permissions.py | 7 ++ taiga/projects/tasks/api.py | 7 +- .../migrations/0008_remove_task_watchers.py | 13 +- taiga/projects/tasks/permissions.py | 7 ++ taiga/projects/tasks/services.py | 6 +- taiga/projects/userstories/api.py | 7 +- .../0010_remove_userstory_watchers.py | 13 +- taiga/projects/userstories/permissions.py | 8 ++ taiga/projects/userstories/services.py | 6 +- taiga/projects/wiki/api.py | 15 ++- .../0002_remove_wikipage_watchers.py | 14 ++- taiga/projects/wiki/permissions.py | 13 +- taiga/projects/wiki/serializers.py | 4 - taiga/routers.py | 15 ++- taiga/timeline/signals.py | 8 +- tests/factories.py | 11 ++ .../test_issues_resources.py | 49 ++++++++ .../test_milestones_resources.py | 91 ++++++++++++++ .../test_projects_resource.py | 53 ++++++++ .../test_tasks_resources.py | 49 ++++++++ .../test_userstories_resources.py | 49 ++++++++ .../test_wiki_resources.py | 95 +++++++++++++- tests/integration/test_issues.py | 4 +- tests/integration/test_notifications.py | 118 ++++++++++++++++++ tests/integration/test_tasks.py | 4 +- tests/integration/test_timeline.py | 72 +++++++++++ tests/integration/test_userstories.py | 4 +- tests/integration/test_watch_issues.py | 76 +++++++++++ tests/integration/test_watch_projects.py | 76 +++++++++++ tests/integration/test_watch_tasks.py | 76 +++++++++++ tests/integration/test_watch_userstories.py | 76 +++++++++++ tests/integration/test_webhooks.py | 23 ++++ 50 files changed, 1205 insertions(+), 134 deletions(-) delete mode 100644 taiga/projects/migrations/0025_remove_project_watchers.py diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 6223adb0..43f78475 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -31,7 +31,7 @@ from taiga.base.api.utils import get_object_or_404 from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -270,6 +270,10 @@ class ProjectFansViewSet(VotersViewSetMixin, ModelListViewSet): resource_model = models.Project +class ProjectWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.ProjectWatchersPermission,) + resource_model = models.Project + ###################################################### ## Custom values for selectors ###################################################### diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index 1007bdf1..5035418b 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -27,7 +27,7 @@ from taiga.base.api.utils import get_object_or_404 from taiga.users.models import User -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.occ import OCCResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin @@ -243,3 +243,8 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W class IssueVotersViewSet(VotersViewSetMixin, ModelListViewSet): permission_classes = (permissions.IssueVotersPermission,) resource_model = models.Issue + + +class IssueWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.IssueWatchersPermission,) + resource_model = models.Issue diff --git a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py index c41e387e..dd3ee037 100644 --- a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py +++ b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM issues_issue_watchers""".format(content_type_id=ContentType.objects.get(model='issue').id)) - + update_all_contenttypes() + sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) +SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id +FROM issues_issue_watchers INNER JOIN issues_issue ON issues_issue_watchers.issue_id = issues_issue.id""".format(content_type_id=ContentType.objects.get(model='issue').id) + cursor = connection.cursor() + cursor.execute(sql) class Migration(migrations.Migration): diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 82120e14..91f988ca 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -52,3 +52,10 @@ class IssueVotersPermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_issues') list_perms = HasProjectPerm('view_issues') + + +class IssueWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_issues') + list_perms = HasProjectPerm('view_issues') diff --git a/taiga/projects/issues/services.py b/taiga/projects/issues/services.py index 9a553cd3..d1156227 100644 --- a/taiga/projects/issues/services.py +++ b/taiga/projects/issues/services.py @@ -27,7 +27,7 @@ from taiga.base.utils import db, text from taiga.projects.issues.apps import ( connect_issues_signals, disconnect_issues_signals) - +from taiga.projects.votes import services as votes_services from . import models @@ -84,7 +84,8 @@ def issues_to_csv(project, queryset): fieldnames = ["ref", "subject", "description", "milestone", "owner", "owner_full_name", "assigned_to", "assigned_to_full_name", "status", "severity", "priority", "type", "is_closed", - "attachments", "external_reference", "tags"] + "attachments", "external_reference", "tags", + "watchers", "voters"] for custom_attr in project.issuecustomattributes.all(): fieldnames.append(custom_attr.name) @@ -108,6 +109,8 @@ def issues_to_csv(project, queryset): "attachments": issue.attachments.count(), "external_reference": issue.external_reference, "tags": ",".join(issue.tags or []), + "watchers": [u.id for u in issue.get_watchers()], + "voters": votes_services.get_voters(issue).count(), } for custom_attr in project.issuecustomattributes.all(): diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 0a5e583e..6c5a4bcc 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -37,6 +37,7 @@ from taiga.projects.wiki.models import * from taiga.projects.attachments.models import * from taiga.projects.custom_attributes.models import * from taiga.projects.history.services import take_snapshot +from taiga.projects.votes.services import add_vote from taiga.events.apps import disconnect_events_signals @@ -97,7 +98,8 @@ NUM_TASKS = getattr(settings, "SAMPLE_DATA_NUM_TASKS", (0, 4)) NUM_USS_BACK = getattr(settings, "SAMPLE_DATA_NUM_USS_BACK", (8, 20)) NUM_ISSUES = getattr(settings, "SAMPLE_DATA_NUM_ISSUES", (12, 25)) NUM_ATTACHMENTS = getattr(settings, "SAMPLE_DATA_NUM_ATTACHMENTS", (0, 4)) - +NUM_VOTES = getattr(settings, "SAMPLE_DATA_NUM_VOTES", (0, 3)) +NUM_PROJECT_WATCHERS = getattr(settings, "SAMPLE_DATA_NUM_PROJECT_WATCHERS", (0, 3)) class Command(BaseCommand): sd = SampleDataHelper(seed=12345678901) @@ -215,6 +217,7 @@ class Command(BaseCommand): project.total_story_points = int(defined_points * self.sd.int(5,12) / 10) project.save() + self.create_votes(project, project) def create_attachment(self, obj, order): attached_file = self.sd.file_from_directory(*ATTACHMENT_SAMPLE_DATA) @@ -287,7 +290,7 @@ class Command(BaseCommand): bug.save() watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - bug.watchers.add(watching_user) + bug.add_watcher(watching_user) take_snapshot(bug, comment=self.sd.paragraph(), @@ -300,6 +303,7 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=bug.owner) + self.create_votes(bug, project) return bug def create_task(self, project, milestone, us, min_date, max_date, closed=False): @@ -338,7 +342,7 @@ class Command(BaseCommand): user=task.owner) watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - task.watchers.add(watching_user) + task.add_watcher(watching_user) # Add history entry task.status=self.sd.db_object_from_queryset(project.task_statuses.all()) @@ -347,6 +351,7 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=task.owner) + self.create_votes(task, project) return task def create_us(self, project, milestone=None, computable_project_roles=[]): @@ -387,7 +392,7 @@ class Command(BaseCommand): us.save() watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - us.watchers.add(watching_user) + us.add_watcher(watching_user) take_snapshot(us, comment=self.sd.paragraph(), @@ -400,6 +405,7 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=us.owner) + self.create_votes(us, project) return us def create_milestone(self, project, start_date, end_date): @@ -434,6 +440,11 @@ class Command(BaseCommand): project.is_kanban_activated = True project.save() take_snapshot(project, user=project.owner) + + for i in range(self.sd.int(*NUM_PROJECT_WATCHERS)): + watching_user = self.sd.db_object_from_queryset(User.objects.all()) + project.add_watcher(watching_user) + return project def create_user(self, counter=None, username=None, full_name=None, email=None): @@ -452,3 +463,8 @@ class Command(BaseCommand): user.save() return user + + def create_votes(self, obj, project): + for i in range(self.sd.int(*NUM_VOTES)): + voting_user=self.sd.db_object_from_queryset(project.members.all()) + add_vote(obj, voting_user) diff --git a/taiga/projects/migrations/0024_auto_20150810_1247.py b/taiga/projects/migrations/0024_auto_20150810_1247.py index ebe758fc..f057816b 100644 --- a/taiga/projects/migrations/0024_auto_20150810_1247.py +++ b/taiga/projects/migrations/0024_auto_20150810_1247.py @@ -14,12 +14,6 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AddField( - model_name='project', - name='watchers', - field=models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, related_name='projects_project+', null=True, verbose_name='watchers'), - preserve_default=True, - ), migrations.AlterField( model_name='project', name='public_permissions', diff --git a/taiga/projects/migrations/0025_remove_project_watchers.py b/taiga/projects/migrations/0025_remove_project_watchers.py deleted file mode 100644 index 5748edc9..00000000 --- a/taiga/projects/migrations/0025_remove_project_watchers.py +++ /dev/null @@ -1,28 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations -from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes - -def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT project_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM projects_project_watchers""".format(content_type_id=ContentType.objects.get(model='project').id)) - - -class Migration(migrations.Migration): - dependencies = [ - ('notifications', '0004_watched'), - ('projects', '0024_auto_20150810_1247'), - ] - - operations = [ - migrations.RunPython(create_notifications), - migrations.RemoveField( - model_name='project', - name='watchers', - ), - ] diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py index 93a09831..3194dc39 100644 --- a/taiga/projects/milestones/api.py +++ b/taiga/projects/milestones/api.py @@ -17,10 +17,10 @@ from taiga.base import filters from taiga.base import response from taiga.base.decorators import detail_route -from taiga.base.api import ModelCrudViewSet +from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api.utils import get_object_or_404 -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.history.mixins import HistoryResourceMixin @@ -36,9 +36,11 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView permission_classes = (permissions.MilestonePermission,) filter_backends = (filters.CanViewMilestonesFilterBackend,) filter_fields = ("project", "closed") + queryset = models.Milestone.objects.all() def get_queryset(self): - qs = models.Milestone.objects.all() + qs = super().get_queryset() + qs = self.attach_watchers_attrs_to_queryset(qs) qs = qs.prefetch_related("user_stories", "user_stories__role_points", "user_stories__role_points__points", @@ -91,3 +93,8 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView optimal_points -= optimal_points_per_day return response.Ok(milestone_stats) + + +class MilestoneWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.MilestoneWatchersPermission,) + resource_model = models.Milestone diff --git a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py index 897f47bf..69d6aacd 100644 --- a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py +++ b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py @@ -1,16 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM milestones_milestone_watchers""".format(content_type_id=ContentType.objects.get(model='milestone').id)), + update_all_contenttypes() + sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) +SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id +FROM milestones_milestone_watchers INNER JOIN milestones_milestone ON milestones_milestone_watchers.milestone_id = milestones_milestone.id""".format(content_type_id=ContentType.objects.get(model='milestone').id) + cursor = connection.cursor() + cursor.execute(sql) class Migration(migrations.Migration): diff --git a/taiga/projects/milestones/permissions.py b/taiga/projects/milestones/permissions.py index 9823c8de..843c0c8a 100644 --- a/taiga/projects/milestones/permissions.py +++ b/taiga/projects/milestones/permissions.py @@ -15,8 +15,8 @@ # along with this program. If not, see . from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsProjectOwner, AllowAny, - PermissionComponent, IsSuperUser) + IsAuthenticated, IsProjectOwner, AllowAny, + IsSuperUser) class MilestonePermission(TaigaResourcePermission): @@ -29,3 +29,11 @@ class MilestonePermission(TaigaResourcePermission): destroy_perms = HasProjectPerm('delete_milestone') list_perms = AllowAny() stats_perms = HasProjectPerm('view_milestones') + watch_perms = IsAuthenticated() & HasProjectPerm('view_milestones') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_milestones') + +class MilestoneWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_milestones') + list_perms = HasProjectPerm('view_milestones') diff --git a/taiga/projects/milestones/serializers.py b/taiga/projects/milestones/serializers.py index a2a483e5..471b9546 100644 --- a/taiga/projects/milestones/serializers.py +++ b/taiga/projects/milestones/serializers.py @@ -17,7 +17,6 @@ from django.utils.translation import ugettext as _ from taiga.base.api import serializers - from taiga.base.utils import json from taiga.projects.notifications.mixins import WatchedResourceModelSerializer from taiga.projects.notifications.validators import WatchersValidator diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py index 2431c7c2..b2f2d260 100644 --- a/taiga/projects/notifications/api.py +++ b/taiga/projects/notifications/api.py @@ -19,8 +19,9 @@ from django.db.models import Q from taiga.base.api import ModelCrudViewSet from taiga.projects.notifications.choices import NotifyLevel +from taiga.projects.notifications.models import Watched from taiga.projects.models import Project - +from taiga.users import services as user_services from . import serializers from . import models from . import permissions @@ -32,9 +33,13 @@ class NotifyPolicyViewSet(ModelCrudViewSet): permission_classes = (permissions.NotifyPolicyPermission,) def _build_needed_notify_policies(self): + watched_content = user_services.get_watched_content_for_user(self.request.user) + watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct() + projects = Project.objects.filter( Q(owner=self.request.user) | - Q(memberships__user=self.request.user) + Q(memberships__user=self.request.user) | + Q(id__in=watched_content_project_ids) ).distinct() for project in projects: @@ -45,5 +50,14 @@ class NotifyPolicyViewSet(ModelCrudViewSet): return models.NotifyPolicy.objects.none() self._build_needed_notify_policies() - qs = models.NotifyPolicy.objects.filter(user=self.request.user) - return qs.distinct() + + # With really want to include the policies related to any content: + # - The user is the owner of the project + # - The user is member of the project + # - The user is watching any object from the project + watched_content = user_services.get_watched_content_for_user(self.request.user) + watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct() + return models.NotifyPolicy.objects.filter(Q(project__owner=self.request.user) | + Q(project__memberships__user=self.request.user) | + Q(project__id__in=watched_content_project_ids) + ).distinct() diff --git a/taiga/projects/notifications/migrations/0004_watched.py b/taiga/projects/notifications/migrations/0004_watched.py index 53c85560..ab0878b7 100644 --- a/taiga/projects/notifications/migrations/0004_watched.py +++ b/taiga/projects/notifications/migrations/0004_watched.py @@ -5,10 +5,6 @@ from django.db import models, migrations from django.conf import settings -def fill_watched_table(apps, schema_editor): - Watched = apps.get_model("notifications", "Watched") - print("test") - class Migration(migrations.Migration): dependencies = [ @@ -21,15 +17,22 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Watched', fields=[ - ('id', models.AutoField(primary_key=True, serialize=False, verbose_name='ID', auto_created=True)), + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('object_id', models.PositiveIntegerField()), ('created_date', models.DateTimeField(verbose_name='created date', auto_now_add=True)), ('content_type', models.ForeignKey(to='contenttypes.ContentType')), ('user', models.ForeignKey(related_name='watched', verbose_name='user', to=settings.AUTH_USER_MODEL)), + ('project', models.ForeignKey(to='projects.Project', verbose_name='project', related_name='watched')), + ], options={ + 'verbose_name': 'Watched', + 'verbose_name_plural': 'Watched', }, bases=(models.Model,), ), - migrations.RunPython(fill_watched_table), + migrations.AlterUniqueTogether( + name='watched', + unique_together=set([('content_type', 'object_id', 'user', 'project')]), + ), ] diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index c5ffbb84..75740a3d 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -19,17 +19,21 @@ from operator import is_not from django.apps import apps from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist from django.db import models from django.utils.translation import ugettext_lazy as _ from taiga.base import response from taiga.base.decorators import detail_route from taiga.base.api import serializers +from taiga.base.api.utils import get_object_or_404 from taiga.base.fields import WatchersField from taiga.projects.notifications import services from taiga.projects.notifications.utils import attach_watchers_to_queryset, attach_is_watched_to_queryset from taiga.users.models import User from . import models +from . serializers import WatcherSerializer + class WatchedResourceMixin: @@ -121,7 +125,6 @@ class WatchedModelMixin(object): that should works in almost all cases. """ return self.project - t def get_watchers(self) -> frozenset: """ @@ -139,6 +142,9 @@ class WatchedModelMixin(object): """ return frozenset(services.get_watchers(self)) + def get_watched(self, user_or_id): + return services.get_watched(user_or_id, type(self)) + def add_watcher(self, user): services.add_watcher(self, user) @@ -209,7 +215,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it if not hasattr(obj, "watchers"): - obj.watchers = services.get_watchers(obj) + obj.watchers = [user.id for user in services.get_watchers(obj)] return super(WatchedResourceModelSerializer, self).to_native(obj) diff --git a/taiga/projects/notifications/models.py b/taiga/projects/notifications/models.py index 753b8878..603bdd85 100644 --- a/taiga/projects/notifications/models.py +++ b/taiga/projects/notifications/models.py @@ -22,7 +22,7 @@ from django.utils import timezone from taiga.projects.history.choices import HISTORY_TYPE_CHOICES -from .choices import NOTIFY_LEVEL_CHOICES +from .choices import NOTIFY_LEVEL_CHOICES, NotifyLevel class NotifyPolicy(models.Model): @@ -84,8 +84,9 @@ class Watched(models.Model): related_name="watched", verbose_name=_("user")) created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, verbose_name=_("created date")) - + project = models.ForeignKey("projects.Project", null=False, blank=False, + verbose_name=_("project"),related_name="watched") class Meta: verbose_name = _("Watched") verbose_name_plural = _("Watched") - unique_together = ("content_type", "object_id", "user") + unique_together = ("content_type", "object_id", "user", "project") diff --git a/taiga/projects/notifications/serializers.py b/taiga/projects/notifications/serializers.py index c60e4bc9..9b0b99cd 100644 --- a/taiga/projects/notifications/serializers.py +++ b/taiga/projects/notifications/serializers.py @@ -17,9 +17,10 @@ import json from taiga.base.api import serializers +from taiga.users.models import User from . import models - +from . import choices class NotifyPolicySerializer(serializers.ModelSerializer): @@ -31,3 +32,11 @@ class NotifyPolicySerializer(serializers.ModelSerializer): def get_project_name(self, obj): return obj.project.name + + +class WatcherSerializer(serializers.ModelSerializer): + full_name = serializers.CharField(source='get_full_name', required=False) + + class Meta: + model = User + fields = ('id', 'username', 'full_name') diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index be19a493..adc94a69 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -20,6 +20,7 @@ from django.apps import apps from django.db.transaction import atomic from django.db import IntegrityError, transaction from django.contrib.contenttypes.models import ContentType +from django.contrib.auth import get_user_model from django.utils import timezone from django.conf import settings from django.utils.translation import ugettext as _ @@ -170,15 +171,19 @@ def get_users_to_notify(obj, *, discard_users=None) -> list: candidates = set() candidates.update(filter(_can_notify_hard, project.members.all())) candidates.update(filter(_can_notify_light, obj.get_watchers())) + candidates.update(filter(_can_notify_light, obj.project.get_watchers())) candidates.update(filter(_can_notify_light, obj.get_participants())) + #TODO: coger los watchers del proyecto que quieren ser notificados por correo + #Filtrar los watchers según su nivel de watched y su nivel en el proyecto + # Remove the changer from candidates if discard_users: candidates = candidates - set(discard_users) - candidates = filter(partial(_filter_by_permissions, obj), candidates) + candidates = set(filter(partial(_filter_by_permissions, obj), candidates)) # Filter disabled and system users - candidates = filter(partial(_filter_notificable), candidates) + candidates = set(filter(partial(_filter_notificable), candidates)) return frozenset(candidates) @@ -285,27 +290,54 @@ def process_sync_notifications(): def get_watchers(obj): - User = apps.get_model("users", "User") - Watched = apps.get_model("notifications", "Watched") - content_type = ContentType.objects.get_for_model(obj) - watching_user_ids = Watched.objects.filter(content_type=content_type, object_id=obj.id).values_list("user__id", flat=True) - return User.objects.filter(id__in=watching_user_ids) + """Get the watchers of an object. + + :param obj: Any Django model instance. + + :return: User queryset object representing the users that voted the object. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + return get_user_model().objects.filter(watched__content_type=obj_type, watched__object_id=obj.id) + + +def get_watched(user_or_id, model): + """Get the objects watched by an user. + + :param user_or_id: :class:`~taiga.users.models.User` instance or id. + :param model: Show only objects of this kind. Can be any Django model class. + + :return: Queryset of objects representing the votes of the user. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + conditions = ('notifications_watched.content_type_id = %s', + '%s.id = notifications_watched.object_id' % model._meta.db_table, + 'notifications_watched.user_id = %s') + + if isinstance(user_or_id, get_user_model()): + user_id = user_or_id.id + else: + user_id = user_or_id + + return model.objects.extra(where=conditions, tables=('notifications_watched',), + params=(obj_type.id, user_id)) def add_watcher(obj, user): """Add a watcher to an object. - If the user is already watching the object nothing happends, so this function can be considered - idempotent. + If the user is already watching the object nothing happents (except if there is a level update), + so this function can be considered idempotent. :param obj: Any Django model instance. :param user: User adding the watch. :class:`~taiga.users.models.User` instance. """ obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) - with atomic(): - watched, created = Watched.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) - if not created: - return + watched, created = Watched.objects.get_or_create(content_type=obj_type, + object_id=obj.id, user=user, project=obj.project) + + notify_policy, _ = apps.get_model("notifications", "NotifyPolicy").objects.get_or_create( + project=obj.project, user=user, defaults={"notify_level": NotifyLevel.watch}) + return watched @@ -319,9 +351,8 @@ def remove_watcher(obj, user): :param user: User removing the watch. :class:`~taiga.users.models.User` instance. """ obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) - with atomic(): - qs = Watched.objects.filter(content_type=obj_type, object_id=obj.id, user=user) - if not qs.exists(): - return + qs = Watched.objects.filter(content_type=obj_type, object_id=obj.id, user=user) + if not qs.exists(): + return - qs.delete() + qs.delete() diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 9cabdc97..1ec6f984 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -75,6 +75,13 @@ class ProjectFansPermission(TaigaResourcePermission): list_perms = HasProjectPerm('view_project') +class ProjectWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_project') + list_perms = HasProjectPerm('view_project') + + class MembershipPermission(TaigaResourcePermission): retrieve_perms = HasProjectPerm('view_project') create_perms = IsProjectOwner() diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 2e2ad619..812458f1 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -24,7 +24,7 @@ from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.projects.models import Project, TaskStatus from django.http import HttpResponse -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.occ import OCCResourceMixin from taiga.projects.votes.mixins.viewsets import VotedResourceMixin, VotersViewSetMixin @@ -177,3 +177,8 @@ class TaskViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, Wa class TaskVotersViewSet(VotersViewSetMixin, ModelListViewSet): permission_classes = (permissions.TaskVotersPermission,) resource_model = models.Task + + +class TaskWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.TaskWatchersPermission,) + resource_model = models.Task diff --git a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py index 813eaad9..4c934957 100644 --- a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py +++ b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py @@ -1,16 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM tasks_task_watchers""".format(content_type_id=ContentType.objects.get(model='task').id)), + update_all_contenttypes() + sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) +SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id +FROM tasks_task_watchers INNER JOIN tasks_task ON tasks_task_watchers.task_id = tasks_task.id""".format(content_type_id=ContentType.objects.get(model='task').id) + cursor = connection.cursor() + cursor.execute(sql) class Migration(migrations.Migration): diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index c9cbd667..cf12a283 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -42,3 +42,10 @@ class TaskVotersPermission(TaigaResourcePermission): global_perms = None retrieve_perms = HasProjectPerm('view_tasks') list_perms = HasProjectPerm('view_tasks') + + +class TaskWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_tasks') + list_perms = HasProjectPerm('view_tasks') diff --git a/taiga/projects/tasks/services.py b/taiga/projects/tasks/services.py index 61225aff..8864d893 100644 --- a/taiga/projects/tasks/services.py +++ b/taiga/projects/tasks/services.py @@ -23,6 +23,7 @@ from taiga.projects.tasks.apps import ( connect_tasks_signals, disconnect_tasks_signals) from taiga.events import events +from taiga.projects.votes import services as votes_services from . import models @@ -95,7 +96,8 @@ def tasks_to_csv(project, queryset): fieldnames = ["ref", "subject", "description", "user_story", "milestone", "owner", "owner_full_name", "assigned_to", "assigned_to_full_name", "status", "is_iocaine", "is_closed", "us_order", - "taskboard_order", "attachments", "external_reference", "tags"] + "taskboard_order", "attachments", "external_reference", "tags", + "watchers", "voters"] for custom_attr in project.taskcustomattributes.all(): fieldnames.append(custom_attr.name) @@ -120,6 +122,8 @@ def tasks_to_csv(project, queryset): "attachments": task.attachments.count(), "external_reference": task.external_reference, "tags": ",".join(task.tags or []), + "watchers": [u.id for u in task.get_watchers()], + "voters": votes_services.get_voters(task).count(), } for custom_attr in project.taskcustomattributes.all(): value = task.custom_attributes_values.attributes_values.get(str(custom_attr.id), None) diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 653fdc56..802e3e73 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -31,7 +31,7 @@ from taiga.base.decorators import list_route from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api.utils import get_object_or_404 -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.occ import OCCResourceMixin from taiga.projects.models import Project, UserStoryStatus @@ -270,3 +270,8 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi class UserStoryVotersViewSet(VotersViewSetMixin, ModelListViewSet): permission_classes = (permissions.UserStoryVotersPermission,) resource_model = models.UserStory + + +class UserStoryWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.UserStoryWatchersPermission,) + resource_model = models.UserStory diff --git a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py index d3e24f62..0d897aca 100644 --- a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py +++ b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py @@ -1,16 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM userstories_userstory_watchers""".format(content_type_id=ContentType.objects.get(model='userstory').id)), + update_all_contenttypes() + sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) +SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id +FROM userstories_userstory_watchers INNER JOIN userstories_userstory ON userstories_userstory_watchers.userstory_id = userstories_userstory.id""".format(content_type_id=ContentType.objects.get(model='userstory').id) + cursor = connection.cursor() + cursor.execute(sql) class Migration(migrations.Migration): diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index 0a1c7b8a..fb9361ab 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -35,8 +35,16 @@ class UserStoryPermission(TaigaResourcePermission): watch_perms = IsAuthenticated() & HasProjectPerm('view_us') unwatch_perms = IsAuthenticated() & HasProjectPerm('view_us') + class UserStoryVotersPermission(TaigaResourcePermission): enought_perms = IsProjectOwner() | IsSuperUser() global_perms = None retrieve_perms = HasProjectPerm('view_us') list_perms = HasProjectPerm('view_us') + + +class UserStoryWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_us') + list_perms = HasProjectPerm('view_us') diff --git a/taiga/projects/userstories/services.py b/taiga/projects/userstories/services.py index 9d913707..c06309be 100644 --- a/taiga/projects/userstories/services.py +++ b/taiga/projects/userstories/services.py @@ -31,6 +31,7 @@ from taiga.projects.userstories.apps import ( disconnect_userstories_signals) from taiga.events import events +from taiga.projects.votes import services as votes_services from . import models @@ -138,7 +139,8 @@ def userstories_to_csv(project,queryset): "created_date", "modified_date", "finish_date", "client_requirement", "team_requirement", "attachments", "generated_from_issue", "external_reference", "tasks", - "tags"] + "tags", + "watchers", "voters"] for custom_attr in project.userstorycustomattributes.all(): fieldnames.append(custom_attr.name) @@ -170,6 +172,8 @@ def userstories_to_csv(project,queryset): "external_reference": us.external_reference, "tasks": ",".join([str(task.ref) for task in us.tasks.all()]), "tags": ",".join(us.tags or []), + "watchers": [u.id for u in us.get_watchers()], + "voters": votes_services.get_voters(us).count(), } for role in us.project.roles.filter(computable=True).order_by('name'): diff --git a/taiga/projects/wiki/api.py b/taiga/projects/wiki/api.py index a2e39eda..dba3ccb0 100644 --- a/taiga/projects/wiki/api.py +++ b/taiga/projects/wiki/api.py @@ -21,13 +21,13 @@ from taiga.base.api.permissions import IsAuthenticated from taiga.base import filters from taiga.base import exceptions as exc from taiga.base import response -from taiga.base.api import ModelCrudViewSet +from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api.utils import get_object_or_404 from taiga.base.decorators import list_route from taiga.projects.models import Project from taiga.mdrender.service import render as mdrender -from taiga.projects.notifications.mixins import WatchedResourceMixin +from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.occ import OCCResourceMixin @@ -43,6 +43,12 @@ class WikiViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, permission_classes = (permissions.WikiPagePermission,) filter_backends = (filters.CanViewWikiPagesFilterBackend,) filter_fields = ("project", "slug") + queryset = models.WikiPage.objects.all() + + def get_queryset(self): + qs = super().get_queryset() + qs = self.attach_watchers_attrs_to_queryset(qs) + return qs @list_route(methods=["GET"]) def by_slug(self, request): @@ -77,6 +83,11 @@ class WikiViewSet(OCCResourceMixin, HistoryResourceMixin, WatchedResourceMixin, super().pre_save(obj) +class WikiWatchersViewSet(WatchersViewSetMixin, ModelListViewSet): + permission_classes = (permissions.WikiPageWatchersPermission,) + resource_model = models.WikiPage + + class WikiLinkViewSet(ModelCrudViewSet): model = models.WikiLink serializer_class = serializers.WikiLinkSerializer diff --git a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py index d0c1c832..f2cb8159 100644 --- a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py +++ b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from django.apps import apps +from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() - migrations.RunSQL(sql=""" -INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id) -SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id -FROM wiki_wikipage_watchers""".format(content_type_id=ContentType.objects.get(model='wikipage').id)), + update_all_contenttypes() + sql=""" +INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) +SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id +FROM wiki_wikipage_watchers INNER JOIN wiki_wikipage ON wiki_wikipage_watchers.wikipage_id = wiki_wikipage.id""".format(content_type_id=ContentType.objects.get(model='wikipage').id) + cursor = connection.cursor() + cursor.execute(sql) class Migration(migrations.Migration): diff --git a/taiga/projects/wiki/permissions.py b/taiga/projects/wiki/permissions.py index 684880a8..c64ac985 100644 --- a/taiga/projects/wiki/permissions.py +++ b/taiga/projects/wiki/permissions.py @@ -15,7 +15,8 @@ # along with this program. If not, see . from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, - IsProjectOwner, AllowAny, IsSuperUser) + IsAuthenticated, IsProjectOwner, AllowAny, + IsSuperUser) class WikiPagePermission(TaigaResourcePermission): @@ -29,6 +30,16 @@ class WikiPagePermission(TaigaResourcePermission): destroy_perms = HasProjectPerm('delete_wiki_page') list_perms = AllowAny() render_perms = AllowAny() + watch_perms = IsAuthenticated() & HasProjectPerm('view_wiki_pages') + unwatch_perms = IsAuthenticated() & HasProjectPerm('view_wiki_pages') + + +class WikiPageWatchersPermission(TaigaResourcePermission): + enought_perms = IsProjectOwner() | IsSuperUser() + global_perms = None + retrieve_perms = HasProjectPerm('view_wiki_pages') + list_perms = HasProjectPerm('view_wiki_pages') + class WikiLinkPermission(TaigaResourcePermission): enought_perms = IsProjectOwner() | IsSuperUser() diff --git a/taiga/projects/wiki/serializers.py b/taiga/projects/wiki/serializers.py index a528fdd8..22c9b8bd 100644 --- a/taiga/projects/wiki/serializers.py +++ b/taiga/projects/wiki/serializers.py @@ -22,10 +22,6 @@ from taiga.mdrender.service import render as mdrender from . import models -from taiga.projects.history import services as history_service - -from taiga.mdrender.service import render as mdrender - class WikiPageSerializer(WatchersValidator, WatchedResourceModelSerializer, serializers.ModelSerializer): html = serializers.SerializerMethodField("get_html") diff --git a/taiga/routers.py b/taiga/routers.py index ff7ceff0..5a587b59 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -49,6 +49,7 @@ router.register(r"notify-policies", NotifyPolicyViewSet, base_name="notification # Projects & Selectors from taiga.projects.api import ProjectViewSet from taiga.projects.api import ProjectFansViewSet +from taiga.projects.api import ProjectWatchersViewSet from taiga.projects.api import MembershipViewSet from taiga.projects.api import InvitationViewSet from taiga.projects.api import UserStoryStatusViewSet @@ -62,6 +63,7 @@ from taiga.projects.api import ProjectTemplateViewSet router.register(r"projects", ProjectViewSet, base_name="projects") router.register(r"projects/(?P\d+)/fans", ProjectFansViewSet, base_name="project-fans") +router.register(r"projects/(?P\d+)/watchers", ProjectWatchersViewSet, base_name="project-watchers") 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") @@ -124,22 +126,33 @@ router.register(r"wiki/attachments", WikiAttachmentViewSet, base_name="wiki-atta # Project components from taiga.projects.milestones.api import MilestoneViewSet +from taiga.projects.milestones.api import MilestoneWatchersViewSet from taiga.projects.userstories.api import UserStoryViewSet from taiga.projects.userstories.api import UserStoryVotersViewSet +from taiga.projects.userstories.api import UserStoryWatchersViewSet from taiga.projects.tasks.api import TaskViewSet from taiga.projects.tasks.api import TaskVotersViewSet +from taiga.projects.tasks.api import TaskWatchersViewSet from taiga.projects.issues.api import IssueViewSet from taiga.projects.issues.api import IssueVotersViewSet -from taiga.projects.wiki.api import WikiViewSet, WikiLinkViewSet +from taiga.projects.issues.api import IssueWatchersViewSet +from taiga.projects.wiki.api import WikiViewSet +from taiga.projects.wiki.api import WikiLinkViewSet +from taiga.projects.wiki.api import WikiWatchersViewSet router.register(r"milestones", MilestoneViewSet, base_name="milestones") +router.register(r"milestones/(?P\d+)/watchers", MilestoneWatchersViewSet, base_name="milestone-watchers") router.register(r"userstories", UserStoryViewSet, base_name="userstories") router.register(r"userstories/(?P\d+)/voters", UserStoryVotersViewSet, base_name="userstory-voters") +router.register(r"userstories/(?P\d+)/watchers", UserStoryWatchersViewSet, base_name="userstory-watchers") router.register(r"tasks", TaskViewSet, base_name="tasks") router.register(r"tasks/(?P\d+)/voters", TaskVotersViewSet, base_name="task-voters") +router.register(r"tasks/(?P\d+)/watchers", TaskWatchersViewSet, base_name="task-watchers") router.register(r"issues", IssueViewSet, base_name="issues") router.register(r"issues/(?P\d+)/voters", IssueVotersViewSet, base_name="issue-voters") +router.register(r"issues/(?P\d+)/watchers", IssueWatchersViewSet, base_name="issue-watchers") router.register(r"wiki", WikiViewSet, base_name="wiki") +router.register(r"wiki/(?P\d+)/watchers", WikiWatchersViewSet, base_name="wiki-watchers") router.register(r"wiki-links", WikiLinkViewSet, base_name="wiki-links") diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index d56db4ac..5673fbbb 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -22,14 +22,12 @@ from taiga.projects.history import services as history_services from taiga.projects.models import Project from taiga.users.models import User from taiga.projects.history.choices import HistoryType +from taiga.projects.notifications import services as notifications_services from taiga.timeline.service import (push_to_timeline, build_user_namespace, build_project_namespace, extract_user_info) -# TODO: Add events to followers timeline when followers are implemented. -# TODO: Add events to project watchers timeline when project watchers are implemented. - def _push_to_timeline(*args, **kwargs): if settings.CELERY_ENABLED: @@ -60,9 +58,9 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d related_people |= User.objects.filter(id=obj.assigned_to_id) ## - Watchers - watchers = getattr(obj, "watchers", None) + watchers = notifications_services.get_watchers(obj) if watchers: - related_people |= obj.get_watchers() + related_people |= watchers ## - Exclude inactive and system users and remove duplicate related_people = related_people.exclude(is_active=False) diff --git a/tests/factories.py b/tests/factories.py index 8a351d49..a0950733 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -441,6 +441,17 @@ class VotesFactory(Factory): object_id = factory.Sequence(lambda n: n) +class WatchedFactory(Factory): + class Meta: + model = "notifications.Watched" + strategy = factory.CREATE_STRATEGY + + content_type = factory.SubFactory("tests.factories.ContentTypeFactory") + object_id = factory.Sequence(lambda n: n) + user = factory.SubFactory("tests.factories.UserFactory") + project = factory.SubFactory("tests.factories.ProjectFactory") + + class ContentTypeFactory(Factory): class Meta: model = "contenttypes.ContentType" diff --git a/tests/integration/resources_permissions/test_issues_resources.py b/tests/integration/resources_permissions/test_issues_resources.py index d9950e00..469efacc 100644 --- a/tests/integration/resources_permissions/test_issues_resources.py +++ b/tests/integration/resources_permissions/test_issues_resources.py @@ -9,6 +9,7 @@ from taiga.base.utils import json from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from taiga.projects.votes.services import add_vote +from taiga.projects.notifications.services import add_watcher from taiga.projects.occ import OCCResourceMixin from unittest import mock @@ -616,3 +617,51 @@ def test_issue_action_unwatch(client, data): assert results == [401, 200, 200, 200, 200] results = helper_test_http_method(client, 'post', private_url2, "", users) assert results == [404, 404, 404, 200, 200] + + +def test_issue_watchers_list(client, data): + public_url = reverse('issue-watchers-list', kwargs={"resource_id": data.public_issue.pk}) + private_url1 = reverse('issue-watchers-list', kwargs={"resource_id": data.private_issue1.pk}) + private_url2 = reverse('issue-watchers-list', kwargs={"resource_id": data.private_issue2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_issue_watchers_retrieve(client, data): + add_watcher(data.public_issue, data.project_owner) + public_url = reverse('issue-watchers-detail', kwargs={"resource_id": data.public_issue.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_issue1, data.project_owner) + private_url1 = reverse('issue-watchers-detail', kwargs={"resource_id": data.private_issue1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_issue2, data.project_owner) + private_url2 = reverse('issue-watchers-detail', kwargs={"resource_id": data.private_issue2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/resources_permissions/test_milestones_resources.py b/tests/integration/resources_permissions/test_milestones_resources.py index 955754f9..40a8c008 100644 --- a/tests/integration/resources_permissions/test_milestones_resources.py +++ b/tests/integration/resources_permissions/test_milestones_resources.py @@ -3,6 +3,7 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json from taiga.projects.milestones.serializers import MilestoneSerializer from taiga.projects.milestones.models import Milestone +from taiga.projects.notifications.services import add_watcher from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS from tests import factories as f @@ -274,3 +275,93 @@ def test_milestone_action_stats(client, data): results = helper_test_http_method(client, 'get', private_url2, None, users) assert results == [401, 403, 403, 200, 200] + + +def test_milestone_action_watch(client, data): + public_url = reverse('milestones-watch', kwargs={"pk": data.public_milestone.pk}) + private_url1 = reverse('milestones-watch', kwargs={"pk": data.private_milestone1.pk}) + private_url2 = reverse('milestones-watch', kwargs={"pk": data.private_milestone2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_milestone_action_unwatch(client, data): + public_url = reverse('milestones-unwatch', kwargs={"pk": data.public_milestone.pk}) + private_url1 = reverse('milestones-unwatch', kwargs={"pk": data.private_milestone1.pk}) + private_url2 = reverse('milestones-unwatch', kwargs={"pk": data.private_milestone2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_milestone_watchers_list(client, data): + public_url = reverse('milestone-watchers-list', kwargs={"resource_id": data.public_milestone.pk}) + private_url1 = reverse('milestone-watchers-list', kwargs={"resource_id": data.private_milestone1.pk}) + private_url2 = reverse('milestone-watchers-list', kwargs={"resource_id": data.private_milestone2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_milestone_watchers_retrieve(client, data): + add_watcher(data.public_milestone, data.project_owner) + public_url = reverse('milestone-watchers-detail', kwargs={"resource_id": data.public_milestone.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_milestone1, data.project_owner) + private_url1 = reverse('milestone-watchers-detail', kwargs={"resource_id": data.private_milestone1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_milestone2, data.project_owner) + private_url2 = reverse('milestone-watchers-detail', kwargs={"resource_id": data.private_milestone2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 888b1ef4..27c08d1f 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -81,6 +81,13 @@ def data(): f.VotesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2) f.VotesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2) + f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_member_with_perms) + f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_owner) + f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_member_with_perms) + f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_owner) + f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_member_with_perms) + f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_owner) + return m @@ -109,6 +116,7 @@ def test_project_update(client, data): project_data = ProjectDetailSerializer(data.private_project2).data project_data["is_private"] = False + project_data = json.dumps(project_data) users = [ @@ -300,6 +308,51 @@ def test_project_fans_retrieve(client, data): assert results == [401, 403, 403, 200, 200] +def test_project_watchers_list(client, data): + public_url = reverse('project-watchers-list', kwargs={"resource_id": data.public_project.pk}) + private1_url = reverse('project-watchers-list', kwargs={"resource_id": data.private_project1.pk}) + private2_url = reverse('project-watchers-list', kwargs={"resource_id": data.private_project2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method_and_count(client, 'get', public_url, None, users) + assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)] + results = helper_test_http_method_and_count(client, 'get', private1_url, None, users) + assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)] + results = helper_test_http_method_and_count(client, 'get', private2_url, None, users) + assert results == [(401, 0), (403, 0), (403, 0), (200, 2), (200, 2)] + + +def test_project_watchers_retrieve(client, data): + public_url = reverse('project-watchers-detail', kwargs={"resource_id": data.public_project.pk, + "pk": data.project_owner.pk}) + private1_url = reverse('project-watchers-detail', kwargs={"resource_id": data.private_project1.pk, + "pk": data.project_owner.pk}) + private2_url = reverse('project-watchers-detail', kwargs={"resource_id": data.private_project2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private1_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private2_url, None, users) + assert results == [401, 403, 403, 200, 200] + + def test_project_action_create_template(client, data): public_url = reverse('projects-create-template', kwargs={"pk": data.public_project.pk}) private1_url = reverse('projects-create-template', kwargs={"pk": data.private_project1.pk}) diff --git a/tests/integration/resources_permissions/test_tasks_resources.py b/tests/integration/resources_permissions/test_tasks_resources.py index bd4d0e09..4a871e8e 100644 --- a/tests/integration/resources_permissions/test_tasks_resources.py +++ b/tests/integration/resources_permissions/test_tasks_resources.py @@ -10,6 +10,7 @@ from taiga.projects.occ import OCCResourceMixin from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from taiga.projects.votes.services import add_vote +from taiga.projects.notifications.services import add_watcher from unittest import mock @@ -571,3 +572,51 @@ def test_task_action_unwatch(client, data): assert results == [401, 200, 200, 200, 200] results = helper_test_http_method(client, 'post', private_url2, "", users) assert results == [404, 404, 404, 200, 200] + + +def test_task_watchers_list(client, data): + public_url = reverse('task-watchers-list', kwargs={"resource_id": data.public_task.pk}) + private_url1 = reverse('task-watchers-list', kwargs={"resource_id": data.private_task1.pk}) + private_url2 = reverse('task-watchers-list', kwargs={"resource_id": data.private_task2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_task_watchers_retrieve(client, data): + add_watcher(data.public_task, data.project_owner) + public_url = reverse('task-watchers-detail', kwargs={"resource_id": data.public_task.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_task1, data.project_owner) + private_url1 = reverse('task-watchers-detail', kwargs={"resource_id": data.private_task1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_task2, data.project_owner) + private_url2 = reverse('task-watchers-detail', kwargs={"resource_id": data.private_task2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/resources_permissions/test_userstories_resources.py b/tests/integration/resources_permissions/test_userstories_resources.py index 219c4e2a..20881aed 100644 --- a/tests/integration/resources_permissions/test_userstories_resources.py +++ b/tests/integration/resources_permissions/test_userstories_resources.py @@ -10,6 +10,7 @@ from taiga.projects.occ import OCCResourceMixin from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals from taiga.projects.votes.services import add_vote +from taiga.projects.notifications.services import add_watcher from unittest import mock @@ -570,3 +571,51 @@ def test_user_story_action_unwatch(client, data): assert results == [401, 200, 200, 200, 200] results = helper_test_http_method(client, 'post', private_url2, "", users) assert results == [404, 404, 404, 200, 200] + + +def test_userstory_watchers_list(client, data): + public_url = reverse('userstory-watchers-list', kwargs={"resource_id": data.public_user_story.pk}) + private_url1 = reverse('userstory-watchers-list', kwargs={"resource_id": data.private_user_story1.pk}) + private_url2 = reverse('userstory-watchers-list', kwargs={"resource_id": data.private_user_story2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_userstory_watchers_retrieve(client, data): + add_watcher(data.public_user_story, data.project_owner) + public_url = reverse('userstory-watchers-detail', kwargs={"resource_id": data.public_user_story.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_user_story1, data.project_owner) + private_url1 = reverse('userstory-watchers-detail', kwargs={"resource_id": data.private_user_story1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_user_story2, data.project_owner) + private_url2 = reverse('userstory-watchers-detail', kwargs={"resource_id": data.private_user_story2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/resources_permissions/test_wiki_resources.py b/tests/integration/resources_permissions/test_wiki_resources.py index cf6089b7..14f2f92b 100644 --- a/tests/integration/resources_permissions/test_wiki_resources.py +++ b/tests/integration/resources_permissions/test_wiki_resources.py @@ -1,10 +1,11 @@ from django.core.urlresolvers import reverse from taiga.base.utils import json +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.projects.notifications.services import add_watcher +from taiga.projects.occ import OCCResourceMixin from taiga.projects.wiki.serializers import WikiPageSerializer, WikiLinkSerializer from taiga.projects.wiki.models import WikiPage, WikiLink -from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS -from taiga.projects.occ import OCCResourceMixin from tests import factories as f from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals @@ -436,3 +437,93 @@ def test_wiki_link_patch(client, data): patch_data = json.dumps({"title": "test"}) results = helper_test_http_method(client, 'patch', private_url2, patch_data, users) assert results == [401, 403, 403, 200, 200] + + +def test_wikipage_action_watch(client, data): + public_url = reverse('wiki-watch', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-watch', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-watch', kwargs={"pk": data.private_wiki_page2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_wikipage_action_unwatch(client, data): + public_url = reverse('wiki-unwatch', kwargs={"pk": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-unwatch', kwargs={"pk": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-unwatch', kwargs={"pk": data.private_wiki_page2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'post', public_url, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url1, "", users) + assert results == [401, 200, 200, 200, 200] + results = helper_test_http_method(client, 'post', private_url2, "", users) + assert results == [404, 404, 404, 200, 200] + + +def test_wikipage_watchers_list(client, data): + public_url = reverse('wiki-watchers-list', kwargs={"resource_id": data.public_wiki_page.pk}) + private_url1 = reverse('wiki-watchers-list', kwargs={"resource_id": data.private_wiki_page1.pk}) + private_url2 = reverse('wiki-watchers-list', kwargs={"resource_id": data.private_wiki_page2.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] + + +def test_wikipage_watchers_retrieve(client, data): + add_watcher(data.public_wiki_page, data.project_owner) + public_url = reverse('wiki-watchers-detail', kwargs={"resource_id": data.public_wiki_page.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_wiki_page1, data.project_owner) + private_url1 = reverse('wiki-watchers-detail', kwargs={"resource_id": data.private_wiki_page1.pk, + "pk": data.project_owner.pk}) + add_watcher(data.private_wiki_page2, data.project_owner) + private_url2 = reverse('wiki-watchers-detail', kwargs={"resource_id": data.private_wiki_page2.pk, + "pk": data.project_owner.pk}) + + users = [ + None, + data.registered_user, + data.project_member_without_perms, + data.project_member_with_perms, + data.project_owner + ] + + results = helper_test_http_method(client, 'get', public_url, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url1, None, users) + assert results == [200, 200, 200, 200, 200] + results = helper_test_http_method(client, 'get', private_url2, None, users) + assert results == [401, 403, 403, 200, 200] diff --git a/tests/integration/test_issues.py b/tests/integration/test_issues.py index 84727427..54722c93 100644 --- a/tests/integration/test_issues.py +++ b/tests/integration/test_issues.py @@ -412,6 +412,6 @@ def test_custom_fields_csv_generation(): data.seek(0) reader = csv.reader(data) row = next(reader) - assert row[16] == attr.name + assert row[18] == attr.name row = next(reader) - assert row[16] == "val1" + assert row[18] == "val1" diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index ab0fdb42..87ea400d 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -211,6 +211,124 @@ def test_users_to_notify(): assert users == {member1.user, issue.get_owner()} +def test_watching_users_to_notify_on_issue_modification_1(): + # If: + # - the user is watching the issue + # - the user is not watching the project + # - the notify policy is watch + # Then: + # - email is sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + issue.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.watch + users = services.get_users_to_notify(issue) + assert users == {watching_user, issue.owner} + + +def test_watching_users_to_notify_on_issue_modification_2(): + # If: + # - the user is watching the issue + # - the user is not watching the project + # - the notify policy is notwatch + # Then: + # - email is sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + issue.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.notwatch + users = services.get_users_to_notify(issue) + assert users == {watching_user, issue.owner} + + +def test_watching_users_to_notify_on_issue_modification_3(): + # If: + # - the user is watching the issue + # - the user is not watching the project + # - the notify policy is ignore + # Then: + # - email is not sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + issue.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.ignore + watching_user_policy.save() + users = services.get_users_to_notify(issue) + assert users == {issue.owner} + + +def test_watching_users_to_notify_on_issue_modification_4(): + # If: + # - the user is not watching the issue + # - the user is watching the project + # - the notify policy is ignore + # Then: + # - email is not sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + project.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.ignore + watching_user_policy.save() + users = services.get_users_to_notify(issue) + assert users == {issue.owner} + + +def test_watching_users_to_notify_on_issue_modification_5(): + # If: + # - the user is not watching the issue + # - the user is watching the project + # - the notify policy is watch + # Then: + # - email is sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + project.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.watch + watching_user_policy.save() + users = services.get_users_to_notify(issue) + assert users == {watching_user, issue.owner} + + +def test_watching_users_to_notify_on_issue_modification_6(): + # If: + # - the user is not watching the issue + # - the user is watching the project + # - the notify policy is notwatch + # Then: + # - email is sent + project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) + issue = f.IssueFactory.create(project=project) + watching_user = f.UserFactory() + project.add_watcher(watching_user) + watching_user_policy = services.get_notify_policy(project, watching_user) + issue.description = "test1" + issue.save() + watching_user_policy.notify_level = NotifyLevel.notwatch + watching_user_policy.save() + users = services.get_users_to_notify(issue) + assert users == {watching_user, issue.owner} + + def test_send_notifications_using_services_method(settings, mail): settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 diff --git a/tests/integration/test_tasks.py b/tests/integration/test_tasks.py index 382bfc6f..08825955 100644 --- a/tests/integration/test_tasks.py +++ b/tests/integration/test_tasks.py @@ -163,6 +163,6 @@ def test_custom_fields_csv_generation(): data.seek(0) reader = csv.reader(data) row = next(reader) - assert row[17] == attr.name + assert row[19] == attr.name row = next(reader) - assert row[17] == "val1" + assert row[19] == "val1" diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 00e61fc3..4620e881 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -196,8 +196,10 @@ def test_create_membership_timeline(): def test_update_project_timeline(): + user_watcher= factories.UserFactory() project = factories.ProjectFactory.create(name="test project timeline") history_services.take_snapshot(project, user=project.owner) + project.add_watcher(user_watcher) project.name = "test project timeline updated" project.save() history_services.take_snapshot(project, user=project.owner) @@ -206,11 +208,18 @@ def test_update_project_timeline(): assert project_timeline[0].data["project"]["name"] == "test project timeline updated" assert project_timeline[0].data["values_diff"]["name"][0] == "test project timeline" assert project_timeline[0].data["values_diff"]["name"][1] == "test project timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "projects.project.change" + assert user_watcher_timeline[0].data["project"]["name"] == "test project timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["name"][0] == "test project timeline" + assert user_watcher_timeline[0].data["values_diff"]["name"][1] == "test project timeline updated" def test_update_milestone_timeline(): + user_watcher= factories.UserFactory() milestone = factories.MilestoneFactory.create(name="test milestone timeline") history_services.take_snapshot(milestone, user=milestone.owner) + milestone.add_watcher(user_watcher) milestone.name = "test milestone timeline updated" milestone.save() history_services.take_snapshot(milestone, user=milestone.owner) @@ -219,11 +228,18 @@ def test_update_milestone_timeline(): assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline updated" assert project_timeline[0].data["values_diff"]["name"][0] == "test milestone timeline" assert project_timeline[0].data["values_diff"]["name"][1] == "test milestone timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "milestones.milestone.change" + assert user_watcher_timeline[0].data["milestone"]["name"] == "test milestone timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["name"][0] == "test milestone timeline" + assert user_watcher_timeline[0].data["values_diff"]["name"][1] == "test milestone timeline updated" def test_update_user_story_timeline(): + user_watcher= factories.UserFactory() user_story = factories.UserStoryFactory.create(subject="test us timeline") history_services.take_snapshot(user_story, user=user_story.owner) + user_story.add_watcher(user_watcher) user_story.subject = "test us timeline updated" user_story.save() history_services.take_snapshot(user_story, user=user_story.owner) @@ -232,11 +248,18 @@ def test_update_user_story_timeline(): assert project_timeline[0].data["userstory"]["subject"] == "test us timeline updated" assert project_timeline[0].data["values_diff"]["subject"][0] == "test us timeline" assert project_timeline[0].data["values_diff"]["subject"][1] == "test us timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "userstories.userstory.change" + assert user_watcher_timeline[0].data["userstory"]["subject"] == "test us timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test us timeline" + assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test us timeline updated" def test_update_issue_timeline(): + user_watcher= factories.UserFactory() issue = factories.IssueFactory.create(subject="test issue timeline") history_services.take_snapshot(issue, user=issue.owner) + issue.add_watcher(user_watcher) issue.subject = "test issue timeline updated" issue.save() history_services.take_snapshot(issue, user=issue.owner) @@ -245,11 +268,18 @@ def test_update_issue_timeline(): assert project_timeline[0].data["issue"]["subject"] == "test issue timeline updated" assert project_timeline[0].data["values_diff"]["subject"][0] == "test issue timeline" assert project_timeline[0].data["values_diff"]["subject"][1] == "test issue timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "issues.issue.change" + assert user_watcher_timeline[0].data["issue"]["subject"] == "test issue timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test issue timeline" + assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test issue timeline updated" def test_update_task_timeline(): + user_watcher= factories.UserFactory() task = factories.TaskFactory.create(subject="test task timeline") history_services.take_snapshot(task, user=task.owner) + task.add_watcher(user_watcher) task.subject = "test task timeline updated" task.save() history_services.take_snapshot(task, user=task.owner) @@ -258,11 +288,18 @@ def test_update_task_timeline(): assert project_timeline[0].data["task"]["subject"] == "test task timeline updated" assert project_timeline[0].data["values_diff"]["subject"][0] == "test task timeline" assert project_timeline[0].data["values_diff"]["subject"][1] == "test task timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "tasks.task.change" + assert user_watcher_timeline[0].data["task"]["subject"] == "test task timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["subject"][0] == "test task timeline" + assert user_watcher_timeline[0].data["values_diff"]["subject"][1] == "test task timeline updated" def test_update_wiki_page_timeline(): + user_watcher= factories.UserFactory() page = factories.WikiPageFactory.create(slug="test wiki page timeline") history_services.take_snapshot(page, user=page.owner) + page.add_watcher(user_watcher) page.slug = "test wiki page timeline updated" page.save() history_services.take_snapshot(page, user=page.owner) @@ -271,6 +308,11 @@ def test_update_wiki_page_timeline(): assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline updated" assert project_timeline[0].data["values_diff"]["slug"][0] == "test wiki page timeline" assert project_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "wiki.wikipage.change" + assert user_watcher_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline updated" + assert user_watcher_timeline[0].data["values_diff"]["slug"][0] == "test wiki page timeline" + assert user_watcher_timeline[0].data["values_diff"]["slug"][1] == "test wiki page timeline updated" def test_update_membership_timeline(): @@ -298,50 +340,80 @@ def test_update_membership_timeline(): def test_delete_project_timeline(): project = factories.ProjectFactory.create(name="test project timeline") + user_watcher= factories.UserFactory() + project.add_watcher(user_watcher) history_services.take_snapshot(project, user=project.owner, delete=True) user_timeline = service.get_project_timeline(project) assert user_timeline[0].event_type == "projects.project.delete" assert user_timeline[0].data["project"]["id"] == project.id + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "projects.project.delete" + assert user_watcher_timeline[0].data["project"]["id"] == project.id def test_delete_milestone_timeline(): milestone = factories.MilestoneFactory.create(name="test milestone timeline") + user_watcher= factories.UserFactory() + milestone.add_watcher(user_watcher) history_services.take_snapshot(milestone, user=milestone.owner, delete=True) project_timeline = service.get_project_timeline(milestone.project) assert project_timeline[0].event_type == "milestones.milestone.delete" assert project_timeline[0].data["milestone"]["name"] == "test milestone timeline" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "milestones.milestone.delete" + assert user_watcher_timeline[0].data["milestone"]["name"] == "test milestone timeline" def test_delete_user_story_timeline(): user_story = factories.UserStoryFactory.create(subject="test us timeline") + user_watcher= factories.UserFactory() + user_story.add_watcher(user_watcher) history_services.take_snapshot(user_story, user=user_story.owner, delete=True) project_timeline = service.get_project_timeline(user_story.project) assert project_timeline[0].event_type == "userstories.userstory.delete" assert project_timeline[0].data["userstory"]["subject"] == "test us timeline" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "userstories.userstory.delete" + assert user_watcher_timeline[0].data["userstory"]["subject"] == "test us timeline" def test_delete_issue_timeline(): issue = factories.IssueFactory.create(subject="test issue timeline") + user_watcher= factories.UserFactory() + issue.add_watcher(user_watcher) history_services.take_snapshot(issue, user=issue.owner, delete=True) project_timeline = service.get_project_timeline(issue.project) assert project_timeline[0].event_type == "issues.issue.delete" assert project_timeline[0].data["issue"]["subject"] == "test issue timeline" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "issues.issue.delete" + assert user_watcher_timeline[0].data["issue"]["subject"] == "test issue timeline" def test_delete_task_timeline(): task = factories.TaskFactory.create(subject="test task timeline") + user_watcher= factories.UserFactory() + task.add_watcher(user_watcher) history_services.take_snapshot(task, user=task.owner, delete=True) project_timeline = service.get_project_timeline(task.project) assert project_timeline[0].event_type == "tasks.task.delete" assert project_timeline[0].data["task"]["subject"] == "test task timeline" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "tasks.task.delete" + assert user_watcher_timeline[0].data["task"]["subject"] == "test task timeline" def test_delete_wiki_page_timeline(): page = factories.WikiPageFactory.create(slug="test wiki page timeline") + user_watcher= factories.UserFactory() + page.add_watcher(user_watcher) history_services.take_snapshot(page, user=page.owner, delete=True) project_timeline = service.get_project_timeline(page.project) assert project_timeline[0].event_type == "wiki.wikipage.delete" assert project_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline" + user_watcher_timeline = service.get_profile_timeline(user_watcher) + assert user_watcher_timeline[0].event_type == "wiki.wikipage.delete" + assert user_watcher_timeline[0].data["wikipage"]["slug"] == "test wiki page timeline" def test_delete_membership_timeline(): diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index d8972da2..6b49568a 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -483,6 +483,6 @@ def test_custom_fields_csv_generation(): data.seek(0) reader = csv.reader(data) row = next(reader) - assert row[24] == attr.name + assert row[26] == attr.name row = next(reader) - assert row[24] == "val1" + assert row[26] == "val1" diff --git a/tests/integration/test_watch_issues.py b/tests/integration/test_watch_issues.py index f51d5075..09ba4f7b 100644 --- a/tests/integration/test_watch_issues.py +++ b/tests/integration/test_watch_issues.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import pytest +import json from django.core.urlresolvers import reverse from .. import factories as f @@ -45,3 +46,78 @@ def test_unwatch_issue(client): response = client.post(url) assert response.status_code == 200 + + +def test_list_issue_watchers(client): + user = f.UserFactory.create() + issue = f.IssueFactory(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=issue, user=user) + url = reverse("issue-watchers-list", args=(issue.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_issue_watcher(client): + user = f.UserFactory.create() + issue = f.IssueFactory(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=issue, user=user) + url = reverse("issue-watchers-detail", args=(issue.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_issue_watchers(client): + user = f.UserFactory.create() + issue = f.IssueFactory(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + url = reverse("issues-detail", args=(issue.id,)) + + f.WatchedFactory.create(content_object=issue, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_issue_is_watched(client): + user = f.UserFactory.create() + issue = f.IssueFactory(owner=user) + f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) + url_detail = reverse("issues-detail", args=(issue.id,)) + url_watch = reverse("issues-watch", args=(issue.id,)) + url_unwatch = reverse("issues-unwatch", args=(issue.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 8bb765ce..358c15f2 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import pytest +import json from django.core.urlresolvers import reverse from .. import factories as f @@ -45,3 +46,78 @@ def test_unwacth_project(client): response = client.post(url) assert response.status_code == 200 + + +def test_list_project_watchers(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=project, user=user) + url = reverse("project-watchers-list", args=(project.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_project_watcher(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=project, user=user) + url = reverse("project-watchers-detail", args=(project.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_project_watchers(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-detail", args=(project.id,)) + + f.WatchedFactory.create(content_object=project, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_project_is_watched(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url_detail = reverse("projects-detail", args=(project.id,)) + url_watch = reverse("projects-watch", args=(project.id,)) + url_unwatch = reverse("projects-unwatch", args=(project.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False diff --git a/tests/integration/test_watch_tasks.py b/tests/integration/test_watch_tasks.py index f62e4c7b..7444a948 100644 --- a/tests/integration/test_watch_tasks.py +++ b/tests/integration/test_watch_tasks.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import pytest +import json from django.core.urlresolvers import reverse from .. import factories as f @@ -45,3 +46,78 @@ def test_unwatch_task(client): response = client.post(url) assert response.status_code == 200 + + +def test_list_task_watchers(client): + user = f.UserFactory.create() + task = f.TaskFactory(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=task, user=user) + url = reverse("task-watchers-list", args=(task.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_task_watcher(client): + user = f.UserFactory.create() + task = f.TaskFactory(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=task, user=user) + url = reverse("task-watchers-detail", args=(task.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_task_watchers(client): + user = f.UserFactory.create() + task = f.TaskFactory(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url = reverse("tasks-detail", args=(task.id,)) + + f.WatchedFactory.create(content_object=task, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_task_is_watched(client): + user = f.UserFactory.create() + task = f.TaskFactory(owner=user) + f.MembershipFactory.create(project=task.project, user=user, is_owner=True) + url_detail = reverse("tasks-detail", args=(task.id,)) + url_watch = reverse("tasks-watch", args=(task.id,)) + url_unwatch = reverse("tasks-unwatch", args=(task.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False diff --git a/tests/integration/test_watch_userstories.py b/tests/integration/test_watch_userstories.py index a6a7123e..cad86151 100644 --- a/tests/integration/test_watch_userstories.py +++ b/tests/integration/test_watch_userstories.py @@ -16,6 +16,7 @@ # along with this program. If not, see . import pytest +import json from django.core.urlresolvers import reverse from .. import factories as f @@ -45,3 +46,78 @@ def test_unwatch_user_story(client): response = client.post(url) assert response.status_code == 200 + + +def test_list_user_story_watchers(client): + user = f.UserFactory.create() + user_story = f.UserStoryFactory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + f.WatchedFactory.create(content_object=user_story, user=user) + url = reverse("userstory-watchers-list", args=(user_story.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_user_story_watcher(client): + user = f.UserFactory.create() + user_story = f.UserStoryFactory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + watch = f.WatchedFactory.create(content_object=user_story, user=user) + url = reverse("userstory-watchers-detail", args=(user_story.id, watch.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == watch.user.id + + +def test_get_user_story_watchers(client): + user = f.UserFactory.create() + user_story = f.UserStoryFactory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url = reverse("userstories-detail", args=(user_story.id,)) + + f.WatchedFactory.create(content_object=user_story, user=user) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + + +def test_get_user_story_is_watched(client): + user = f.UserFactory.create() + user_story = f.UserStoryFactory(owner=user) + f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) + url_detail = reverse("userstories-detail", args=(user_story.id,)) + url_watch = reverse("userstories-watch", args=(user_story.id,)) + url_unwatch = reverse("userstories-unwatch", args=(user_story.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False + + response = client.post(url_watch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [user.id] + assert response.data['is_watched'] == True + + response = client.post(url_unwatch) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watched'] == False diff --git a/tests/integration/test_webhooks.py b/tests/integration/test_webhooks.py index 0b3b32f0..9f4ecd71 100644 --- a/tests/integration/test_webhooks.py +++ b/tests/integration/test_webhooks.py @@ -90,3 +90,26 @@ def test_new_object_with_two_webhook(settings): with patch('taiga.webhooks.tasks.delete_webhook') as delete_webhook_mock: services.take_snapshot(obj, user=obj.owner, comment="test", delete=True) assert delete_webhook_mock.call_count == 2 + + +def test_send_request_one_webhook(settings): + settings.WEBHOOKS_ENABLED = True + project = f.ProjectFactory() + f.WebhookFactory.create(project=project) + + objects = [ + f.IssueFactory.create(project=project), + f.TaskFactory.create(project=project), + f.UserStoryFactory.create(project=project), + f.WikiPageFactory.create(project=project) + ] + + for obj in objects: + with patch('taiga.webhooks.tasks._send_request') as _send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test") + assert _send_request_mock.call_count == 1 + + for obj in objects: + with patch('taiga.webhooks.tasks._send_request') as _send_request_mock: + services.take_snapshot(obj, user=obj.owner, comment="test", delete=True) + assert _send_request_mock.call_count == 1 From 3492b46cc9d19a85b8f9e630a224e51276ea37ef Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 18 Aug 2015 09:23:20 +0200 Subject: [PATCH 103/190] User favourites API --- taiga/users/api.py | 27 +++ taiga/users/permissions.py | 1 + taiga/users/serializers.py | 57 +++++- taiga/users/services.py | 171 ++++++++++++++++++ .../test_users_resources.py | 12 ++ tests/integration/test_users.py | 165 +++++++++++++++++ 6 files changed, 432 insertions(+), 1 deletion(-) diff --git a/taiga/users/api.py b/taiga/users/api.py index 0cd8f50c..ea5e5bdc 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -114,6 +114,33 @@ class UsersViewSet(ModelCrudViewSet): self.check_permissions(request, "stats", user) return response.Ok(services.get_stats_for_user(user, request.user)) + @detail_route(methods=["GET"]) + def favourites(self, request, *args, **kwargs): + for_user = get_object_or_404(models.User, **kwargs) + from_user = request.user + self.check_permissions(request, 'favourites', for_user) + filters = { + "type": request.GET.get("type", None), + "action": request.GET.get("action", None), + "q": request.GET.get("q", None), + } + + self.object_list = services.get_favourites_list(for_user, from_user, **filters) + page = self.paginate_queryset(self.object_list) + + extra_args = { + "many": True, + "user_votes": services.get_voted_content_for_user(request.user), + "user_watching": services.get_watched_content_for_user(request.user), + } + + if page is not None: + serializer = serializers.FavouriteSerializer(page.object_list, **extra_args) + else: + serializer = serializers.FavouriteSerializer(self.object_list, **extra_args) + + return response.Ok(serializer.data) + @list_route(methods=["POST"]) def password_recovery(self, request, pk=None): username_or_email = request.DATA.get('username', None) diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index bad16f1a..63c54751 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -47,6 +47,7 @@ class UserPermission(TaigaResourcePermission): starred_perms = AllowAny() change_email_perms = AllowAny() contacts_perms = AllowAny() + favourites_perms = AllowAny() class RolesPermission(TaigaResourcePermission): diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 115462be..3158f5f8 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -19,11 +19,14 @@ from django.core.exceptions import ValidationError from django.utils.translation import ugettext_lazy as _ from taiga.base.api import serializers -from taiga.base.fields import PgArrayField +from taiga.base.fields import PgArrayField, TagsField + from taiga.projects.models import Project from .models import User, Role from .services import get_photo_or_gravatar_url, get_big_photo_or_gravatar_url +from collections import namedtuple + import re @@ -149,3 +152,55 @@ class ProjectRoleSerializer(serializers.ModelSerializer): model = Role fields = ('id', 'name', 'slug', 'order', 'computable') i18n_fields = ("name",) + + +###################################################### +## Favourite +###################################################### + + +class FavouriteSerializer(serializers.Serializer): + type = serializers.CharField() + action = serializers.CharField() + id = serializers.IntegerField() + ref = serializers.IntegerField() + slug = serializers.CharField() + subject = serializers.CharField() + tags = TagsField(default=[]) + project = serializers.IntegerField() + assigned_to = serializers.IntegerField() + total_watchers = serializers.IntegerField() + + is_voted = serializers.SerializerMethodField("get_is_voted") + is_watched = serializers.SerializerMethodField("get_is_watched") + + created_date = serializers.DateTimeField() + + project_name = serializers.CharField() + project_slug = serializers.CharField() + project_is_private = serializers.CharField() + + assigned_to_username = serializers.CharField() + assigned_to_full_name = serializers.CharField() + assigned_to_photo = serializers.SerializerMethodField("get_photo") + + total_votes = serializers.IntegerField() + + def __init__(self, *args, **kwargs): + # Don't pass the extra ids args up to the superclass + self.user_votes = kwargs.pop("user_votes", {}) + self.user_watching = kwargs.pop("user_watching", {}) + + # Instantiate the superclass normally + super(FavouriteSerializer, self).__init__(*args, **kwargs) + + def get_is_voted(self, obj): + return obj["id"] in self.user_votes.get(obj["type"], []) + + def get_is_watched(self, obj): + return obj["id"] in self.user_watching.get(obj["type"], []) + + def get_photo(self, obj): + UserData = namedtuple("UserData", ["photo", "email"]) + user_data = UserData(photo=obj["assigned_to_photo"], email=obj.get("assigned_to_email") or "") + return get_photo_or_gravatar_url(user_data) diff --git a/taiga/users/services.py b/taiga/users/services.py index caf27226..992f4ae8 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -20,6 +20,7 @@ This model contains a domain logic for users application. from django.apps import apps from django.db.models import Q +from django.db import connection from django.conf import settings from django.utils.translation import ugettext as _ @@ -142,3 +143,173 @@ def get_stats_for_user(from_user, by_user): 'total_num_closed_userstories': total_num_closed_userstories, } return project_stats + + +def get_voted_content_for_user(user): + """Returns a dict where: + - The key is the content_type model + - The values are list of id's of the different objects voted by the user + """ + if user.is_anonymous(): + return {} + + user_votes = {} + for (ct_model, object_id) in user.votes.values_list("content_type__model", "object_id"): + list = user_votes.get(ct_model, []) + list.append(object_id) + user_votes[ct_model] = list + + return user_votes + + +def get_watched_content_for_user(user): + """Returns a dict where: + - The key is the content_type model + - The values are list of id's of the different objects watched by the user + """ + if user.is_anonymous(): + return {} + + user_watches = {} + for (ct_model, object_id) in user.watched.values_list("content_type__model", "object_id"): + list = user_watches.get(ct_model, []) + list.append(object_id) + user_watches[ct_model] = list + + return user_watches + + +def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", + project_column="project_id", assigned_to_column="assigned_to_id", + slug_column="slug", subject_column="subject"): + sql = """ + SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'watch' AS action, + tags, notifications_watched.object_id AS object_id, {table_name}.{project_column} AS project, + {slug_column} AS slug, {subject_column} AS subject, + notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to + FROM notifications_watched + INNER JOIN django_content_type + ON (notifications_watched.content_type_id = django_content_type.id AND django_content_type.model = '{type}') + INNER JOIN {table_name} + ON ({table_name}.id = notifications_watched.object_id) + LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers + ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id + LEFT JOIN votes_votes + ON ({table_name}.id = votes_votes.object_id AND django_content_type.id = votes_votes.content_type_id) + WHERE notifications_watched.user_id = {for_user_id} + UNION + SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'vote' AS action, + tags, votes_vote.object_id AS object_id, {table_name}.{project_column} AS project, + {slug_column} AS slug, {subject_column} AS subject, + votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to + FROM votes_vote + INNER JOIN django_content_type + ON (votes_vote.content_type_id = django_content_type.id AND django_content_type.model = '{type}') + INNER JOIN {table_name} + ON ({table_name}.id = votes_vote.object_id) + LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers + ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id + LEFT JOIN votes_votes + ON ({table_name}.id = votes_votes.object_id AND django_content_type.id = votes_votes.content_type_id) + WHERE votes_vote.user_id = {for_user_id} + """ + sql = sql.format(for_user_id=for_user.id, type=type, table_name=table_name, + ref_column = ref_column, project_column=project_column, + assigned_to_column=assigned_to_column, slug_column=slug_column, + subject_column=subject_column) + return sql + + +def get_favourites_list(for_user, from_user, type=None, action=None, q=None): + filters_sql = "" + and_needed = False + + if type: + filters_sql += " AND type = '{type}' ".format(type=type) + + if action: + filters_sql += " AND action = '{action}' ".format(action=action) + + if q: + filters_sql += " AND to_tsvector(coalesce(subject, '')) @@ plainto_tsquery('{q}') ".format(q=q) + + sql = """ + -- BEGIN Basic info: we need to mix info from different tables and denormalize it + SELECT entities.*, + projects_project.name as project_name, projects_project.slug as project_slug, projects_project.is_private as project_is_private, + users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email + FROM ( + {userstories_sql} + UNION + {tasks_sql} + UNION + {issues_sql} + UNION + {projects_sql} + ) as entities + -- END Basic info + + -- BEGIN Project info + LEFT JOIN projects_project + ON (entities.project = projects_project.id) + -- END Project info + + -- BEGIN Assigned to user info + LEFT JOIN users_user + ON (assigned_to = users_user.id) + -- END Assigned to user info + + -- BEGIN Permissions checking + LEFT JOIN projects_membership + -- Here we check the memberbships from the user requesting the info + ON (projects_membership.user_id = {from_user_id} AND projects_membership.project_id = entities.project) + + LEFT JOIN users_role + ON (entities.project = users_role.project_id AND users_role.id = projects_membership.role_id) + + WHERE + -- public project + ( + projects_project.is_private = false + OR( + -- private project where the view_ permission is included in the user role for that project or in the anon permissions + projects_project.is_private = true + AND( + (entities.type = 'issue' AND 'view_issues' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + OR (entities.type = 'task' AND 'view_tasks' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + OR (entities.type = 'userstory' AND 'view_us' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + OR (entities.type = 'project' AND 'view_project' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + ) + )) + -- END Permissions checking + {filters_sql} + + ORDER BY entities.created_date; + """ + + from_user_id = -1 + if not from_user.is_anonymous(): + from_user_id = from_user.id + + sql = sql.format( + for_user_id=for_user.id, + from_user_id=from_user_id, + filters_sql=filters_sql, + userstories_sql=_build_favourites_sql_for_type(for_user, "userstory", "userstories_userstory", slug_column="null"), + tasks_sql=_build_favourites_sql_for_type(for_user, "task", "tasks_task", slug_column="null"), + issues_sql=_build_favourites_sql_for_type(for_user, "issue", "issues_issue", slug_column="null"), + projects_sql=_build_favourites_sql_for_type(for_user, "project", "projects_project", + ref_column="null", + project_column="id", + assigned_to_column="null", + subject_column="projects_project.name") + ) + + cursor = connection.cursor() + cursor.execute(sql) + + desc = cursor.description + return [ + dict(zip([col[0] for col in desc], row)) + for row in cursor.fetchall() + ] diff --git a/tests/integration/resources_permissions/test_users_resources.py b/tests/integration/resources_permissions/test_users_resources.py index fada3a72..761439d1 100644 --- a/tests/integration/resources_permissions/test_users_resources.py +++ b/tests/integration/resources_permissions/test_users_resources.py @@ -287,3 +287,15 @@ def test_user_action_change_email(client, data): after_each_request() results = helper_test_http_method(client, 'post', url, patch_data, users, after_each_request=after_each_request) assert results == [204, 204, 204] + + +def test_user_list_votes(client, data): + url = reverse('users-favourites', kwargs={"pk": data.registered_user.pk}) + users = [ + None, + data.registered_user, + data.other_user, + data.superuser, + ] + results = helper_test_http_method(client, 'get', url, None, users) + assert results == [200, 200, 200, 200] diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 2d3f1664..b658045f 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -1,6 +1,7 @@ import pytest from tempfile import NamedTemporaryFile +from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse from .. import factories as f @@ -9,6 +10,7 @@ from taiga.base.utils import json from taiga.users import models from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS +from taiga.users.services import get_favourites_list pytestmark = pytest.mark.django_db @@ -249,3 +251,166 @@ def test_list_contacts_public_projects(client): response_content = response.data assert len(response_content) == 1 assert response_content[0]["id"] == user_2.id + + +def test_get_favourites_list(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) + project.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(project) + f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=project.id, count=1) + + user_story = f.UserStoryFactory(project=project, subject="Testing user story") + user_story.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(user_story) + f.VoteFactory(content_type=content_type, object_id=user_story.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=user_story.id, count=1) + + task = f.TaskFactory(project=project, subject="Testing task") + task.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(task) + f.VoteFactory(content_type=content_type, object_id=task.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=task.id, count=1) + + issue = f.IssueFactory(project=project, subject="Testing issue") + issue.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(issue) + f.VoteFactory(content_type=content_type, object_id=issue.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=issue.id, count=1) + + assert len(get_favourites_list(fav_user, viewer_user)) == 8 + assert len(get_favourites_list(fav_user, viewer_user, type="project")) == 2 + assert len(get_favourites_list(fav_user, viewer_user, type="userstory")) == 2 + assert len(get_favourites_list(fav_user, viewer_user, type="task")) == 2 + assert len(get_favourites_list(fav_user, viewer_user, type="issue")) == 2 + assert len(get_favourites_list(fav_user, viewer_user, type="unknown")) == 0 + + assert len(get_favourites_list(fav_user, viewer_user, action="watch")) == 4 + assert len(get_favourites_list(fav_user, viewer_user, action="vote")) == 4 + + assert len(get_favourites_list(fav_user, viewer_user, q="issue")) == 2 + assert len(get_favourites_list(fav_user, viewer_user, q="unexisting text")) == 0 + + +def test_get_favourites_list_valid_info_for_project(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + watcher_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + project.add_watcher(watcher_user) + content_type = ContentType.objects.get_for_model(project) + vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=project.id, count=1) + + project_vote_info = get_favourites_list(fav_user, viewer_user)[0] + assert project_vote_info["type"] == "project" + assert project_vote_info["action"] == "vote" + assert project_vote_info["id"] == project.id + assert project_vote_info["ref"] == None + assert project_vote_info["slug"] == project.slug + assert project_vote_info["subject"] == project.name + assert project_vote_info["tags"] == project.tags + assert project_vote_info["project"] == project.id + assert project_vote_info["assigned_to"] == None + assert project_vote_info["total_watchers"] == 1 + assert project_vote_info["created_date"] == vote.created_date + assert project_vote_info["project_name"] == project.name + assert project_vote_info["project_slug"] == project.slug + assert project_vote_info["project_is_private"] == project.is_private + assert project_vote_info["assigned_to_username"] == None + assert project_vote_info["assigned_to_full_name"] == None + assert project_vote_info["assigned_to_photo"] == None + assert project_vote_info["assigned_to_email"] == None + assert project_vote_info["total_votes"] == 1 + + +def test_get_favourites_list_valid_info_for_not_project_types(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + watcher_user = f.UserFactory() + assigned_to_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + + factories = { + "userstory": f.UserStoryFactory, + "task": f.TaskFactory, + "issue": f.IssueFactory + } + + for object_type in factories: + instance = factories[object_type](project=project, + subject="Testing", + tags=["test1", "test2"], + assigned_to=assigned_to_user) + + instance.add_watcher(watcher_user) + content_type = ContentType.objects.get_for_model(instance) + vote = f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) + + instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0] + assert instance_vote_info["type"] == object_type + assert instance_vote_info["action"] == "vote" + assert instance_vote_info["id"] == instance.id + assert instance_vote_info["ref"] == instance.ref + assert instance_vote_info["slug"] == None + assert instance_vote_info["subject"] == instance.subject + assert instance_vote_info["tags"] == instance.tags + assert instance_vote_info["project"] == instance.project.id + assert instance_vote_info["assigned_to"] == assigned_to_user.id + assert instance_vote_info["total_watchers"] == 1 + assert instance_vote_info["created_date"] == vote.created_date + assert instance_vote_info["project_name"] == instance.project.name + assert instance_vote_info["project_slug"] == instance.project.slug + assert instance_vote_info["project_is_private"] == instance.project.is_private + assert instance_vote_info["assigned_to_username"] == assigned_to_user.username + assert instance_vote_info["assigned_to_full_name"] == assigned_to_user.full_name + assert instance_vote_info["assigned_to_photo"] == '' + assert instance_vote_info["assigned_to_email"] == assigned_to_user.email + assert instance_vote_info["total_votes"] == 3 + + +def test_get_favourites_list_permissions(): + fav_user = f.UserFactory() + viewer_unpriviliged_user = f.UserFactory() + viewer_priviliged_user = f.UserFactory() + + project = f.ProjectFactory(is_private=True, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=viewer_priviliged_user) + content_type = ContentType.objects.get_for_model(project) + f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=project.id, count=1) + + user_story = f.UserStoryFactory(project=project, subject="Testing user story") + content_type = ContentType.objects.get_for_model(user_story) + f.VoteFactory(content_type=content_type, object_id=user_story.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=user_story.id, count=1) + + task = f.TaskFactory(project=project, subject="Testing task") + content_type = ContentType.objects.get_for_model(task) + f.VoteFactory(content_type=content_type, object_id=task.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=task.id, count=1) + + issue = f.IssueFactory(project=project, subject="Testing issue") + content_type = ContentType.objects.get_for_model(issue) + f.VoteFactory(content_type=content_type, object_id=issue.id, user=fav_user) + f.VotesFactory(content_type=content_type, object_id=issue.id, count=1) + + #If the project is private a viewer user without any permission shouldn' see any vote + assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 0 + + #If the project is private but the viewer user has permissions the votes should be accesible + assert len(get_favourites_list(fav_user, viewer_priviliged_user)) == 4 + + #If the project is private but has the required anon permissions the votes should be accesible by any user too + project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] + project.save() + assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 4 From 3111cf79eb5642b24ed57bbd0b0465f8e7b853ba Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 18 Aug 2015 10:49:05 +0200 Subject: [PATCH 104/190] Improving admin --- taiga/projects/admin.py | 5 ++++- taiga/projects/issues/admin.py | 5 ++++- taiga/projects/milestones/admin.py | 3 +++ taiga/projects/notifications/admin.py | 25 +++++++++++++++++++++++++ taiga/projects/tasks/admin.py | 5 ++++- taiga/projects/userstories/admin.py | 4 +++- taiga/projects/votes/admin.py | 25 +++++++++++++++++++++++++ taiga/projects/votes/models.py | 2 +- taiga/projects/wiki/admin.py | 5 ++++- 9 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 taiga/projects/notifications/admin.py create mode 100644 taiga/projects/votes/admin.py diff --git a/taiga/projects/admin.py b/taiga/projects/admin.py index f3eea353..5dcc9758 100644 --- a/taiga/projects/admin.py +++ b/taiga/projects/admin.py @@ -17,7 +17,10 @@ from django.contrib import admin from taiga.projects.milestones.admin import MilestoneInline +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline from taiga.users.admin import RoleInline + from . import models class MembershipAdmin(admin.ModelAdmin): @@ -35,7 +38,7 @@ class ProjectAdmin(admin.ModelAdmin): list_display = ["name", "owner", "created_date", "total_milestones", "total_story_points"] list_display_links = list_display - inlines = [RoleInline, MembershipInline, MilestoneInline] + inlines = [RoleInline, MembershipInline, MilestoneInline, WatchedInline, VoteInline] def get_object(self, *args, **kwargs): self.obj = super().get_object(*args, **kwargs) diff --git a/taiga/projects/issues/admin.py b/taiga/projects/issues/admin.py index 16da297e..fe891719 100644 --- a/taiga/projects/issues/admin.py +++ b/taiga/projects/issues/admin.py @@ -17,13 +17,16 @@ from django.contrib import admin from taiga.projects.attachments.admin import AttachmentInline +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline + from . import models class IssueAdmin(admin.ModelAdmin): list_display = ["project", "milestone", "ref", "subject",] list_display_links = ["ref", "subject",] - # inlines = [AttachmentInline] + inlines = [WatchedInline, VoteInline] def get_object(self, *args, **kwargs): self.obj = super().get_object(*args, **kwargs) diff --git a/taiga/projects/milestones/admin.py b/taiga/projects/milestones/admin.py index b741a1b2..52919e87 100644 --- a/taiga/projects/milestones/admin.py +++ b/taiga/projects/milestones/admin.py @@ -15,6 +15,8 @@ # along with this program. If not, see . from django.contrib import admin +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline from . import models @@ -30,6 +32,7 @@ class MilestoneAdmin(admin.ModelAdmin): list_display_links = list_display list_filter = ["project"] readonly_fields = ["owner"] + inlines = [WatchedInline, VoteInline] admin.site.register(models.Milestone, MilestoneAdmin) diff --git a/taiga/projects/notifications/admin.py b/taiga/projects/notifications/admin.py new file mode 100644 index 00000000..2dc22d61 --- /dev/null +++ b/taiga/projects/notifications/admin.py @@ -0,0 +1,25 @@ +# 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 django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from . import models + + +class WatchedInline(GenericTabularInline): + model = models.Watched + extra = 0 diff --git a/taiga/projects/tasks/admin.py b/taiga/projects/tasks/admin.py index 937e70a3..5295e7ca 100644 --- a/taiga/projects/tasks/admin.py +++ b/taiga/projects/tasks/admin.py @@ -17,6 +17,9 @@ from django.contrib import admin from taiga.projects.attachments.admin import AttachmentInline +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline + from . import models @@ -24,7 +27,7 @@ class TaskAdmin(admin.ModelAdmin): list_display = ["project", "milestone", "user_story", "ref", "subject",] list_display_links = ["ref", "subject",] list_filter = ["project"] - # inlines = [AttachmentInline] + inlines = [WatchedInline, VoteInline] def get_object(self, *args, **kwargs): self.obj = super().get_object(*args, **kwargs) diff --git a/taiga/projects/userstories/admin.py b/taiga/projects/userstories/admin.py index cd23dc35..9cb3e667 100644 --- a/taiga/projects/userstories/admin.py +++ b/taiga/projects/userstories/admin.py @@ -17,6 +17,8 @@ from django.contrib import admin from taiga.projects.attachments.admin import AttachmentInline +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline from . import models @@ -41,7 +43,7 @@ class UserStoryAdmin(admin.ModelAdmin): list_display = ["project", "milestone", "ref", "subject",] list_display_links = ["ref", "subject",] list_filter = ["project"] - inlines = [RolePointsInline] + inlines = [RolePointsInline, WatchedInline, VoteInline] def get_object(self, *args, **kwargs): self.obj = super().get_object(*args, **kwargs) diff --git a/taiga/projects/votes/admin.py b/taiga/projects/votes/admin.py new file mode 100644 index 00000000..4e06ae8f --- /dev/null +++ b/taiga/projects/votes/admin.py @@ -0,0 +1,25 @@ +# 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 django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from . import models + + +class VoteInline(GenericTabularInline): + model = models.Vote + extra = 0 diff --git a/taiga/projects/votes/models.py b/taiga/projects/votes/models.py index 7fcf857a..a1af1ff7 100644 --- a/taiga/projects/votes/models.py +++ b/taiga/projects/votes/models.py @@ -63,4 +63,4 @@ class Vote(models.Model): return None def __str__(self): - return self.user + return self.user.get_full_name() diff --git a/taiga/projects/wiki/admin.py b/taiga/projects/wiki/admin.py index cb846105..ca929fb3 100644 --- a/taiga/projects/wiki/admin.py +++ b/taiga/projects/wiki/admin.py @@ -17,6 +17,9 @@ from django.contrib import admin from taiga.projects.attachments.admin import AttachmentInline +from taiga.projects.notifications.admin import WatchedInline +from taiga.projects.votes.admin import VoteInline + from taiga.projects.wiki.models import WikiPage from . import models @@ -24,7 +27,7 @@ from . import models class WikiPageAdmin(admin.ModelAdmin): list_display = ["project", "slug", "owner"] list_display_links = list_display - # inlines = [AttachmentInline] + inlines = [WatchedInline, VoteInline] admin.site.register(models.WikiPage, WikiPageAdmin) From e1ac780911b8f721d1c5e7b1d405ead706623a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 27 Aug 2015 08:52:21 +0200 Subject: [PATCH 105/190] [i18n] Update locales --- taiga/locale/pt_BR/LC_MESSAGES/django.po | 218 ++++++++++++++++++----- 1 file changed, 175 insertions(+), 43 deletions(-) diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index a013a6cb..18724850 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -14,7 +14,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-24 23:00+0000\n" +"PO-Revision-Date: 2015-08-25 15:53+0000\n" "Last-Translator: Renato Prado \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" @@ -1103,7 +1103,7 @@ msgstr "Ver projeto" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 #: taiga/permissions/permissions.py:54 msgid "View milestones" -msgstr "" +msgstr "Ver marco de progresso" #: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 msgid "View user stories" @@ -1175,15 +1175,15 @@ msgstr "Modificar wiki link" #: taiga/permissions/permissions.py:55 msgid "Add milestone" -msgstr "" +msgstr "Adicionar marco de progresso" #: taiga/permissions/permissions.py:56 msgid "Modify milestone" -msgstr "" +msgstr "modificar marco de progresso" #: taiga/permissions/permissions.py:57 msgid "Delete milestone" -msgstr "" +msgstr "remover marco de progresso" #: taiga/permissions/permissions.py:59 msgid "View user story" @@ -1580,7 +1580,7 @@ msgstr "prioridade" #: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 #: taiga/projects/userstories/models.py:60 msgid "milestone" -msgstr "" +msgstr "marco de progresso" #: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 msgid "finished date" @@ -1704,7 +1704,7 @@ msgstr "members" #: taiga/projects/models.py:139 msgid "total of milestones" -msgstr "" +msgstr "total de marcos de progresso" #: taiga/projects/models.py:140 msgid "total story points" @@ -1884,6 +1884,10 @@ msgid "" "Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" "See issue #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"Caso atualizado\n" +"Olá %(user)s, %(changer)s atualizou um caso em %(project)s\n" +"Ver caso #%(ref)s %(subject)s em %(url)s\n" #: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 #, python-format @@ -1891,6 +1895,8 @@ msgid "" "\n" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Atualizou um caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 #, python-format @@ -1940,6 +1946,8 @@ msgid "" "\n" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Criou um caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 #, python-format @@ -1984,6 +1992,8 @@ msgid "" "\n" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Removeu o caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 #, python-format @@ -1997,6 +2007,14 @@ msgid "" "Taiga\">See sprint\n" " " msgstr "" +"\n" +"

Sprint atualizado

\n" +"

Olá %(user)s,
%(changer)s atualizou um sprint em %(project)s\n" +"

Sprint %(name)s

\n" +" Ver sprint\n" +" " #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 #, python-format @@ -2006,6 +2024,10 @@ msgid "" "Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" "See sprint %(name)s at %(url)s\n" msgstr "" +"\n" +"Sprint atualizado\n" +"Olá %(user)s, %(changer)s atualizou sprint em %(project)s\n" +"Ver sprint %(name)s em %(url)s\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 #, python-format @@ -2013,6 +2035,8 @@ msgid "" "\n" "[%(project)s] Updated the sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Atualizou o sprint \"%(milestone)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 #, python-format @@ -2062,6 +2086,8 @@ msgid "" "\n" "[%(project)s] Created the sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Criou o sprint \"%(milestone)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 #, python-format @@ -2106,6 +2132,8 @@ msgid "" "\n" "[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" msgstr "" +"\n" +"[%(project)s] Removeu o Sprint \"%(milestone)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 #, python-format @@ -2119,6 +2147,13 @@ msgid "" "%(subject)s in Taiga\">See task\n" " " msgstr "" +"\n" +"

Tarefa atualizada

\n" +"

Olá %(user)s,
%(changer)s atualizou tarefa em %(project)s

\n" +"

Tarefa #%(ref)s %(subject)s

\n" +" Ver tarefa\n" +" " #: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 #, python-format @@ -2128,6 +2163,10 @@ msgid "" "Hello %(user)s, %(changer)s has updated a task on %(project)s\n" "See task #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"Tarefa atualizada\n" +"Olá %(user)s, %(changer)s atualizou tarefa em %(project)s\n" +"Ver tarefa #%(ref)s %(subject)s em %(url)s\n" #: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 #, python-format @@ -2135,6 +2174,8 @@ msgid "" "\n" "[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Atualizou a tarefa #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 #, python-format @@ -2183,6 +2224,8 @@ msgid "" "\n" "[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Criou a tarefa #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 #, python-format @@ -2227,6 +2270,8 @@ msgid "" "\n" "[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Deletou a tarefa #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 #, python-format @@ -2240,6 +2285,14 @@ msgid "" "%(subject)s in Taiga\">See user story\n" " " msgstr "" +"\n" +"

User Story atualizada

\n" +"

Olá %(user)s,
%(changer)s atualizou a user story em %(project)s\n" +"

User Story #%(ref)s %(subject)s

\n" +" Ver user story\n" +" " #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 #, python-format @@ -2249,6 +2302,10 @@ msgid "" "Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" "See user story #%(ref)s %(subject)s at %(url)s\n" msgstr "" +"\n" +"User story atualizada\n" +"Olá %(user)s, %(changer)s atualizou a user story em %(project)s\n" +"Ver user story #%(ref)s %(subject)s em %(url)s\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 #, python-format @@ -2256,6 +2313,8 @@ msgid "" "\n" "[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Atualizou a US #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 #, python-format @@ -2305,6 +2364,8 @@ msgid "" "\n" "[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Criou a US #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 #, python-format @@ -2350,6 +2411,8 @@ msgid "" "\n" "[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" msgstr "" +"\n" +"[%(project)s] Removeu a US #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 #, python-format @@ -2363,6 +2426,14 @@ msgid "" "\">See Wiki Page\n" " " msgstr "" +"\n" +"

Página Wiki modificada

\n" +"

Olá %(user)s,
%(changer)s atualizou a página wiki em%(project)s\n" +"

Página Wiki %(page)s

\n" +" Ver página Wiki\n" +" " #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 #, python-format @@ -2374,6 +2445,12 @@ msgid "" "\n" "See wiki page %(page)s at %(url)s\n" msgstr "" +"\n" +"Página Wiki modificada\n" +"\n" +"Olá %(user)s, %(changer)s atualizou a página wiki em %(project)s\n" +"\n" +"Ver página wiki %(page)s em %(url)s\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 #, python-format @@ -2381,6 +2458,8 @@ msgid "" "\n" "[%(project)s] Updated the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] atualizou a página wiki \"%(page)s\"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 #, python-format @@ -2434,6 +2513,8 @@ msgid "" "\n" "[%(project)s] Created the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] Criou a página wiki \"%(page)s\"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 #, python-format @@ -2483,6 +2564,8 @@ msgid "" "\n" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" msgstr "" +"\n" +"[%(project)s] Removeu a página Wiki \"%(page)s\"\n" #: taiga/projects/notifications/validators.py:44 msgid "Watchers contains invalid users" @@ -2518,7 +2601,7 @@ msgstr "Função inválida para projeto" #: taiga/projects/serializers.py:348 msgid "Total milestones must be major or equal to zero" -msgstr "" +msgstr "Total de marcos de progresso devem ser maior ou igual a zero" #: taiga/projects/serializers.py:405 msgid "Default options" @@ -2607,6 +2690,12 @@ msgid "" "Management Tool.

\n" " " msgstr "" +"\n" +"

Você foi convidado para o Taiga!

\n" +"

Oi! %(full_name)s te enviou um convite para se juntar ao projeto " +"%(project)s no Taiga.
Taiga é uma ferramenta de gerenciamento de " +"projetos ágil, código aberto e grátis.

\n" +" " #: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 #, python-format @@ -2617,6 +2706,11 @@ msgid "" "

%(extra)s

\n" " " msgstr "" +"\n" +"

E agora algumas palavrãos do bom companheiros ou " +"companheiras
que vieram tão gentilmente convidá-lo

\n" +"

%(extra)s

\n" +" " #: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 msgid "Accept your invitation to Taiga" @@ -2640,6 +2734,12 @@ msgid "" "%(project)s which is being managed on Taiga, a Free, open Source Agile " "Project Management Tool.\n" msgstr "" +"\n" +"Você ou algum conhecido te convidou para o Taiga\n" +"\n" +"Oi! %(full_name)s te enviou um convite para se juntar ao projeto chamado " +"%(project)s que está começando a ser gerenciado no Taiga, um gratuíto, " +"ferramenta de gerenciamento de projetos ágeis de código aberto.\n" #: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 #, python-format @@ -2651,6 +2751,12 @@ msgid "" "%(extra)s\n" " " msgstr "" +"\n" +"E agora algumas palavras do bom companheiro ou companheiras que pensavam " +"como tão gentilmente como poderiam convidá-lo:\n" +"\n" +"%(extra)s\n" +" " #: taiga/projects/templates/emails/membership_invitation-body-text.jinja:18 msgid "Accept your invitation to Taiga following this link:" @@ -2735,6 +2841,12 @@ msgid "" "then allowed to grow and change as more is learned about the product and its " "customers" msgstr "" +"O backlog no scrum é uma lista de funcionalidades priorizadas, contendo " +"pequenas descrições de todas as funcionalidades desejadas no produto. Quando " +"se aplicada ao scrum, não é necessário começar com um longo, esforço " +"inicial para documentar todos os requisitos. O backlog é então permitido " +"crescer e modificar-se no processo que é compreendido sobre o produto e seus " +"consumidores." #. Translators: Name of kanban project template. #: taiga/projects/translations.py:33 @@ -2749,6 +2861,10 @@ msgid "" "process, from definition of a task to its delivery to the customer, is " "displayed for participants to see and team members pull work from a queue." msgstr "" +"Kanban é um método de gerenciamento intelectual com ênfase em entregas just-" +"in-time, não sobrecarregando membros dos times. Nessa abordagem, o processo, " +"da definição da tarefa a entrega para o consumidor, os participantes podem " +"visualizar os próprios membros do time pegar o trabalho de uma lista." #. Translators: User story point value (value = undefined) #: taiga/projects/translations.py:43 @@ -2966,6 +3082,8 @@ msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " "{subject}\")" msgstr "" +"Gerando a user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " +"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" @@ -3006,11 +3124,11 @@ msgstr "Não há projeto com esse id" #: taiga/projects/validators.py:37 msgid "There's no user story status with that id" -msgstr "" +msgstr "Não há status de user story com aquele id" #: taiga/projects/validators.py:46 msgid "There's no task status with that id" -msgstr "" +msgstr "Não há status de tarega com aquele id" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:54 @@ -3027,15 +3145,15 @@ msgstr "Vote" #: taiga/projects/wiki/api.py:60 msgid "'content' parameter is mandatory" -msgstr "" +msgstr "parâmetro 'conteúdo' é mandatório" #: taiga/projects/wiki/api.py:63 msgid "'project_id' parameter is mandatory" -msgstr "" +msgstr "parametro 'project_id' é mandatório" #: taiga/projects/wiki/models.py:36 msgid "last modifier" -msgstr "" +msgstr "último modificador" #: taiga/projects/wiki/models.py:69 msgid "href" @@ -3043,82 +3161,86 @@ msgstr "href" #: taiga/timeline/signals.py:88 msgid "Check the history API for the exact diff" -msgstr "" +msgstr "Verifique o histórico da API para a exata diferença" #: taiga/users/admin.py:50 msgid "Personal info" -msgstr "" +msgstr "Informação pessoal" #: taiga/users/admin.py:52 msgid "Permissions" -msgstr "" +msgstr "Permissões" #: taiga/users/admin.py:53 msgid "Important dates" -msgstr "" +msgstr "Datas importantes" #: taiga/users/api.py:124 taiga/users/api.py:131 msgid "Invalid username or email" -msgstr "" +msgstr "usuário ou e-mail inválido" #: taiga/users/api.py:140 msgid "Mail sended successful!" -msgstr "" +msgstr "E-mail enviado com sucesso" #: taiga/users/api.py:152 taiga/users/api.py:157 msgid "Token is invalid" -msgstr "" +msgstr "Token é inválido" #: taiga/users/api.py:178 msgid "Current password parameter needed" -msgstr "" +msgstr "parâmetro de senha atual necessário" #: taiga/users/api.py:181 msgid "New password parameter needed" -msgstr "" +msgstr "Parâmetro de nova senha necessário" #: taiga/users/api.py:184 msgid "Invalid password length at least 6 charaters needed" -msgstr "" +msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" #: taiga/users/api.py:187 msgid "Invalid current password" -msgstr "" +msgstr "senha atual inválida" #: taiga/users/api.py:203 msgid "Incomplete arguments" -msgstr "" +msgstr "argumentos incompletos" #: taiga/users/api.py:208 msgid "Invalid image format" -msgstr "" +msgstr "formato de imagem inválida" #: taiga/users/api.py:261 msgid "Duplicated email" -msgstr "" +msgstr "e-mail duplicado" #: taiga/users/api.py:263 msgid "Not valid email" -msgstr "" +msgstr "Não é um e-mail válido" #: taiga/users/api.py:283 taiga/users/api.py:289 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" +"Inválido, você está certo que o token está correto e não foi usado " +"anteriormente?" #: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 msgid "Invalid, are you sure the token is correct?" -msgstr "" +msgstr "Inválido, está certo que o token está correto?" #: taiga/users/models.py:69 msgid "superuser status" -msgstr "" +msgstr "status de superuser" #: taiga/users/models.py:70 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" +"Designa que esse usuário tem todas as permissões sem explicitamente assiná-" +"las" #: taiga/users/models.py:100 msgid "username" @@ -3127,7 +3249,7 @@ msgstr "usuário" #: taiga/users/models.py:101 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" -msgstr "" +msgstr "Requerido. 30 caracteres ou menos. Letras, números e caracteres /./-/_" #: taiga/users/models.py:104 msgid "Enter a valid username." @@ -3142,6 +3264,8 @@ msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" +"Designa quando esse usuário deve ser tratado como ativo. desmarque isso em " +"vez de deletar contas." #: taiga/users/models.py:114 msgid "biography" @@ -3153,27 +3277,27 @@ msgstr "foto" #: taiga/users/models.py:118 msgid "date joined" -msgstr "" +msgstr "data ingressado" #: taiga/users/models.py:120 msgid "default language" -msgstr "" +msgstr "lingua padrão" #: taiga/users/models.py:122 msgid "default theme" -msgstr "" +msgstr "tema padrão" #: taiga/users/models.py:124 msgid "default timezone" -msgstr "" +msgstr "fuso horário padrão" #: taiga/users/models.py:126 msgid "colorize tags" -msgstr "" +msgstr "tags coloridas" #: taiga/users/models.py:131 msgid "email token" -msgstr "" +msgstr "token de e-mail" #: taiga/users/models.py:133 msgid "new email address" @@ -3189,11 +3313,11 @@ msgstr "inválido" #: taiga/users/serializers.py:70 msgid "Invalid username. Try with a different one." -msgstr "" +msgstr "Usuário inválido. Tente com um diferente." #: taiga/users/services.py:48 taiga/users/services.py:52 msgid "Username or password does not matches user." -msgstr "" +msgstr "Usuário ou senha não correspondem ao usuário" #: taiga/users/templates/emails/change_email-body-html.jinja:4 #, python-format @@ -3241,7 +3365,7 @@ msgstr "" #: taiga/users/templates/emails/change_email-subject.jinja:1 msgid "[Taiga] Change email" -msgstr "" +msgstr "[Taiga] Troca de email" #: taiga/users/templates/emails/password_recovery-body-html.jinja:4 #, python-format @@ -3290,7 +3414,7 @@ msgstr "" #: taiga/users/templates/emails/password_recovery-subject.jinja:1 msgid "[Taiga] Password recovery" -msgstr "" +msgstr "[Taiga] Recuperação de senha" #: taiga/users/templates/emails/registered_user-body-html.jinja:6 msgid "" @@ -3330,6 +3454,11 @@ msgid "" "here\n" " " msgstr "" +"\n" +" Você pode remover sua conta desse serviço clicando " +"aqui\n" +" " #: taiga/users/templates/emails/registered_user-body-text.jinja:1 msgid "" @@ -3369,19 +3498,22 @@ msgid "" "\n" "You may remove your account from this service: %(url)s\n" msgstr "" +"\n" +"Você já pode remover sua conta desse serviço: %(url)s\n" #: taiga/users/templates/emails/registered_user-subject.jinja:1 msgid "You've been Taigatized!" -msgstr "" +msgstr "Você foi Taigueado!" #: taiga/users/validators.py:29 msgid "There's no role with that id" -msgstr "" +msgstr "Não há função com esse id" #: taiga/userstorage/api.py:50 msgid "" "Duplicate key value violates unique constraint. Key '{}' already exists." msgstr "" +"Valor de chave duplicada viola regra de limitação. Chave '{}' já existe." #: taiga/userstorage/models.py:30 msgid "key" From 0e792d75b16536c86f8ec5dd6a0f5bef15c31192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 28 Aug 2015 10:26:07 +0200 Subject: [PATCH 106/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 405 ++++++++++---------- taiga/locale/de/LC_MESSAGES/django.po | 407 ++++++++++---------- taiga/locale/en/LC_MESSAGES/django.po | 411 +++++++++++---------- taiga/locale/es/LC_MESSAGES/django.po | 407 ++++++++++---------- taiga/locale/fi/LC_MESSAGES/django.po | 405 ++++++++++---------- taiga/locale/fr/LC_MESSAGES/django.po | 405 ++++++++++---------- taiga/locale/nl/LC_MESSAGES/django.po | 405 ++++++++++---------- taiga/locale/pl/LC_MESSAGES/django.po | 407 ++++++++++---------- taiga/locale/pt_BR/LC_MESSAGES/django.po | 407 ++++++++++---------- taiga/locale/ru/LC_MESSAGES/django.po | 405 ++++++++++---------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 405 ++++++++++---------- 11 files changed, 2339 insertions(+), 2130 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 731e1b2b..1d7c7581 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" @@ -32,11 +32,11 @@ msgstr "Sistema de registre invàlid" msgid "invalid login type" msgstr "Sistema de login invàlid" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "nom d'usuari invàlid" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Requerit. 255 caràcters o menys. Lletres, nombres i caràcters /./-/_" @@ -329,12 +329,12 @@ msgstr "Error d'integritat per argument invàlid o erroni." msgid "Precondition error" msgstr "Precondició errònia." -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "" -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "" @@ -521,24 +521,24 @@ msgstr "" msgid "error importing timelines" msgstr "" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Contingut invàlid. Deu ser {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "" @@ -712,8 +712,9 @@ msgstr "Comentari" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "Data de creació" @@ -783,8 +784,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no és un arxiu json vàlid" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "El projecte no existeix" @@ -925,12 +926,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Veure projecte" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Veure fita" @@ -938,167 +939,179 @@ msgstr "Veure fita" msgid "View user stories" msgstr "Veure història d'usuari" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Veure tasca" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Veure incidència" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Veure pàgina del wiki" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Veure links del wiki" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Vota incidéncies" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Demana membresía" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Afegeix història d'usuari a projecte" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Afegeix comentaris a històries d'usuari" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Afegeix comentaris a tasques" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Afegeix incidéncies" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Afegeix comentaris a incidéncies" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Afegeix pàgina del wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Modifica pàgina del wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Afegeix enllaç de wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Modifica enllaç de wiki" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Afegeix fita" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Modifica fita" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Borra fita" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Veure història d'usuari" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Afegeix història d'usuari" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Modifica història d'usuari" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Borra història d'usuari" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Afegeix tasca" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Modifica tasca" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Borra tasca" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Afegeix incidència" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Modifica incidència" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Borra incidència" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Borra pàgina de wiki" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Borra enllaç de wiki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Modifica projecte" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Afegeix membre" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Borra membre" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Borra projecte" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Administrar valors de projecte" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Administrar rols" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Al menys un del usuaris ha de ser administrador" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." @@ -1111,8 +1124,8 @@ msgid "Project ID not matches between object and project" msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1121,11 +1134,12 @@ msgstr "Amo" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1142,7 +1156,7 @@ msgstr "Id d'objecte" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1159,18 +1173,18 @@ msgstr "està obsolet " #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "Descripció" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "Ordre" @@ -1200,11 +1214,11 @@ msgid "Multi-Line Text" msgstr "" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "Nom" @@ -1368,23 +1382,23 @@ msgstr "nota de bloqueig" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tens permissos per a ficar aquest sprint a aquesta incidència" -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "No tens permissos per a ficar aquest status a aquesta tasca" -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "No tens permissos per a ficar aquesta severitat a aquesta tasca" -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "No tens permissos per a ficar aquesta prioritat a aquesta incidència" -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "No tens permissos per a ficar aquest tipus a aquesta incidència" @@ -1430,9 +1444,9 @@ msgstr "assignada a" msgid "external reference" msgstr "referència externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1445,8 +1459,8 @@ msgstr "Data estimada d'inici" msgid "estimated finish date" msgstr "Data estimada de finalització" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "està tancat" @@ -1475,175 +1489,175 @@ msgstr "" msgid "'project' parameter is mandatory" msgstr "" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "email" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "text extra d'invitació" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "L'usuari ja es membre del projecte" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "Points per defecte" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "estatus d'història d'usuai per defecte" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "Estatus de tasca per defecte" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "Prioritat per defecte" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "Severitat per defecte" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "Status d'incidència per defecte" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "Tipus d'incidència per defecte" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "membres" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "total de fites" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "total de punts d'història" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "activa panell de backlog" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "activa panell de kanban" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "activa panell de wiki" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "activa panell d'incidències" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "sistema de videoconferència" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "template de creació" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "permisos d'anònims" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "permisos d'usuaris" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "es privat" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "colors de tags" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "configuració de mòdules" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "està arxivat" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "color" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "limit de treball en progrés" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "rol d'amo per defecte" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "opcions per defecte" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "status d'històries d'usuari" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "punts" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "status de tasques" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "status d'incidències" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "tipus d'incidències" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "prioritats" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "severitats" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "rols" @@ -1659,28 +1673,33 @@ msgstr "Observant" msgid "Ignoring" msgstr "Ignorant" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "Observadors" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "creada data" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "Actualitzada data" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "" @@ -2161,7 +2180,7 @@ msgstr "" "\n" "[%(project)s] Borrada pàgina de Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "" @@ -2185,51 +2204,51 @@ msgstr "Versió" msgid "You can't leave the project if there are no more owners" msgstr "No pots deixar el projecte si no hi ha més amos" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Aquest e-mail ja està en ús" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rol invàlid per al projecte" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Opcions per defecte" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Estatus d'històries d'usuari" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Punts" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Estatus de tasques" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Estatus d'incidéncies" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Tipus d'incidéncies" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Prioritats" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Severitats" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Rols" @@ -2241,15 +2260,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2627,15 +2646,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2689,24 +2708,24 @@ msgstr "No hi ha cap estatis d'història d'usuari amb eixe id" msgid "There's no task status with that id" msgstr "No hi ha cap estatus de tasca amb eixe id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Vots" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "vots" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Vot" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "" @@ -2718,7 +2737,7 @@ msgstr "últim a modificar" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -2734,57 +2753,57 @@ msgstr "Permissos" msgid "Important dates" msgstr "Dates importants" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Nom d'usuari o email invàlid" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "Correu enviat satisfactòriament" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Token invàlid" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Paràmetre de password actual requerit" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Paràmetre de password requerit" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Password invàlid, al menys 6 caràcters requerits" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Password actual invàlid" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Arguments incomplets." -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Format d'image invàlid" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Email duplicat" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Email no vàlid" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invàlid. Estás segur que el token es correcte i que no l'has usat abans?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Invàlid. Estás segur que el token es correcte?" @@ -2865,15 +2884,15 @@ msgstr "nova adreça de correu" msgid "permissions" msgstr "permissos" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "invàlid" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Nom d'usuari invàlid" -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 767924bb..748f3192 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-16 08:52+0000\n" -"Last-Translator: Henning Matthaei\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -38,11 +38,11 @@ msgstr "Ungültige Registrierungsart" msgid "invalid login type" msgstr "Ungültige Loginart" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "Ungültiger Benutzername" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" @@ -359,12 +359,12 @@ msgstr "Integritätsfehler wegen falscher oder ungültiger Argumente" msgid "Precondition error" msgstr "Voraussetzungsfehler" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Fehler in Filter Parameter Typen." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' muss ein Integer-Wert sein." @@ -574,24 +574,24 @@ msgstr "Fehler beim Importieren der Schlagworte" msgid "error importing timelines" msgstr "Fehler beim Importieren der Chroniken" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" wurde in diesem Projekt nicht gefunden" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Invalider Inhalt. Er muss wie folgt sein: {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" @@ -858,8 +858,9 @@ msgstr "Kommentar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "Erstellungsdatum" @@ -929,8 +930,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Die Nutzlast ist kein gültiges json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Das Projekt existiert nicht" @@ -1095,12 +1096,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Projekt ansehen" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Meilensteine ansehen" @@ -1108,167 +1109,179 @@ msgstr "Meilensteine ansehen" msgid "View user stories" msgstr "User-Stories ansehen. " -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Aufgaben ansehen" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Tickets ansehen" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Wiki Seiten ansehen" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Wiki Links ansehen" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Tickets auswählen" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Mitgliedschaft beantragen" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "User-Story zu Projekt hinzufügen" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Kommentar zu User-Stories hinzufügen" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Kommentare zu Aufgaben hinzufügen" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Tickets hinzufügen" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Kommentare zu Tickets hinzufügen" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Wiki Seite hinzufügen" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Wiki Seite ändern" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Wiki Link hinzufügen" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Wiki Link ändern" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Meilenstein hinzufügen" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Meilenstein ändern" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Meilenstein löschen" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "User-Story ansehen" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "User-Story hinzufügen" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "User-Story ändern" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "User-Story löschen" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Aufgabe hinzufügen" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Aufgabe ändern" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Aufgabe löschen" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Ticket hinzufügen" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Ticket ändern" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Gelöschtes Ticket" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Wiki Seite löschen" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Wiki Link löschen" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Projekt ändern" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Mitglied hinzufügen" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Mitglied entfernen" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Projekt löschen" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Administrator Projekt Werte" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Administrator-Rollen" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Unglültiger Templatename" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Ungültige Templatebeschreibung" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Mindestens ein Benutzer muss ein aktiver Administrator sein. " -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" @@ -1281,8 +1294,8 @@ msgid "Project ID not matches between object and project" msgstr "Nr. unterschreidet sich zwischen dem Objekt und dem Projekt" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1291,11 +1304,12 @@ msgstr "Besitzer" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1312,7 +1326,7 @@ msgstr "Objekt Nr." #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1329,18 +1343,18 @@ msgstr "wurde verworfen" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "Beschreibung" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "Reihenfolge" @@ -1370,11 +1384,11 @@ msgid "Multi-Line Text" msgstr "Mehrzeiliger Text" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "Name" @@ -1538,27 +1552,27 @@ msgstr "Blockierungsgrund" msgid "sprint" msgstr "Sprint" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Sprint zu setzen." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Status zu setzen. " -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Gewichtung zu setzen." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Priorität zu setzen. " -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Sie haben nicht die Berechtigung, das Ticket auf diese Art zu setzen." @@ -1604,9 +1618,9 @@ msgstr "zugewiesen an" msgid "external reference" msgstr "externe Referenz" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "Slug" @@ -1619,8 +1633,8 @@ msgstr "geschätzter Starttermin" msgid "estimated finish date" msgstr "geschätzter Endtermin" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "ist geschlossen" @@ -1649,175 +1663,175 @@ msgstr "'{param}' Parameter ist ein Pflichtfeld" msgid "'project' parameter is mandatory" msgstr "Der 'project' Parameter ist ein Pflichtfeld" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "E-Mail" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "erstellt am " -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "Token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "Einladung Zusatztext " -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "Benutzerreihenfolge" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "Der Benutzer ist bereits Mitglied dieses Projekts" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "voreingestellte Punkte" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "voreingesteller User-Story Status " -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "voreingestellter Aufgabenstatus" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "voreingestellte Priorität " -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "voreingestellte Gewichtung " -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "voreingestellter Ticket Status" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "voreingestellter Ticket Typ" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "Mitglieder" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "Meilensteine Gesamt" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "Story Punkte insgesamt" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "aktives Backlog Panel" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "aktives Kanban Panel" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "aktives Wiki Panel" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "aktives Tickets Panel" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "Videokonferenzsystem" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "Rechte für anonyme Nutzer" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "Rechte für registrierte Nutzer" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "ist privat" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "Tag Farben" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "Module konfigurieren" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "ist archiviert" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "Farbe" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "Ausführungslimit" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "Wert" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "voreingestellte Besitzerrolle" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "Vorgabe Optionen" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "User-Story Status " -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "Punkte" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "Aufgaben Status" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "Ticket Status" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "Ticket Arten" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "Prioritäten" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "Gewichtung" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "Rollen" @@ -1833,28 +1847,33 @@ msgstr "Beobachten" msgid "Ignoring" msgstr "Ignorieren" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "Beobachter" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "Chronik Einträge" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "Benutzer benachrichtigen" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "" @@ -2613,7 +2632,7 @@ msgstr "" "[%(project)s] löschte die Wiki Seite \"%(page)s\"\n" "\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Beobachter enthält ungültige Benutzer " @@ -2639,51 +2658,51 @@ msgstr "" "Sie können das Projekt nicht verlassen, wenn keine weiteren Besitzer " "vorhanden sind" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Die E-Mailadresse ist bereits vergeben" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Ungültige Rolle für dieses Projekt" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Die Gesamtzahl der Meilensteine muss größer oder gleich Null sein " -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Voreingestellte Optionen" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Status für User-Stories" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Punkte" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Aufgaben Status" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Ticket Status" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Ticket Arten" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Prioritäten" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Gewichtung" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Rollen" @@ -2695,18 +2714,18 @@ msgstr "Zukünftiger Sprint" msgid "Project End" msgstr "Projektende" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" "Sie haben nicht die Berechtigung, diesen Sprint auf diese Aufgabe zu setzen" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" "Sie haben nicht die Berechtigung, diese User-Story auf diese Aufgabe zu " "setzen" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" "Sie haben nicht die Berechtigung, diesen Status auf diese Aufgabe zu setzen." @@ -3102,19 +3121,19 @@ msgstr "Projekteigentümer " msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Sie haben nicht die Berechtigung, diesen Sprint auf diese User-Story zu " "setzen." -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" "Sie haben nicht die Berechtigung, diesen Status auf diese User-Story zu " "setzen." -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3168,24 +3187,24 @@ msgstr "Es gibt keinen User-Story Status mit dieser id" msgid "There's no task status with that id" msgstr "Es gibt keinen Aufgabenstatus mit dieser id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Stimmen" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "Stimmen" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Stimme" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "'content' Parameter ist erforderlich" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "'project_id' Parameter ist erforderlich" @@ -3197,7 +3216,7 @@ msgstr "letzte Änderung" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -3213,58 +3232,58 @@ msgstr "Berechtigungen" msgid "Important dates" msgstr "Wichtige Termine" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Ungültiger Benutzername oder E-Mail" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "E-Mail erfolgreich gesendet." -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Token ist ungültig" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Aktueller Passwort Parameter wird benötigt" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Neuer Passwort Parameter benötigt" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Ungültige Passwortlänge, mindestens 6 Zeichen erforderlich" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Ungültiges aktuelles Passwort" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Unvollständige Argumente" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Ungültiges Bildformat" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Doppelte E-Mail" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Ungültige E-Mail" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Ungültig. Sind Sie sicher, dass das Token korrekt ist und Sie es nicht " "bereits verwendet haben?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Ungültig. Sind Sie sicher, dass das Token korrekt ist?" @@ -3346,15 +3365,15 @@ msgstr "neue E-Mail Adresse" msgid "permissions" msgstr "Berechtigungen" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "ungültig" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Ungültiger Benutzername. Versuchen Sie es mit einem anderen." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Benutzername oder Passwort stimmen mit keinem Benutzer überein." diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 4901c6a8..4d0c20ce 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -28,11 +28,11 @@ msgstr "" msgid "invalid login type" msgstr "" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" @@ -321,12 +321,12 @@ msgstr "" msgid "Precondition error" msgstr "" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "" -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "" @@ -510,24 +510,24 @@ msgstr "" msgid "error importing timelines" msgstr "" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "" -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "" @@ -701,8 +701,9 @@ msgstr "" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "" @@ -756,8 +757,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "" @@ -898,12 +899,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "" @@ -911,167 +912,179 @@ msgstr "" msgid "View user stories" msgstr "" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "" -#: taiga/permissions/permissions.py:55 -msgid "Add milestone" +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" msgstr "" -#: taiga/permissions/permissions.py:56 -msgid "Modify milestone" +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" msgstr "" -#: taiga/permissions/permissions.py:57 -msgid "Delete milestone" +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" msgstr "" #: taiga/permissions/permissions.py:59 -msgid "View user story" +msgid "Add milestone" msgstr "" #: taiga/permissions/permissions.py:60 -msgid "Add user story" +msgid "Modify milestone" msgstr "" #: taiga/permissions/permissions.py:61 -msgid "Modify user story" +msgid "Delete milestone" msgstr "" -#: taiga/permissions/permissions.py:62 -msgid "Delete user story" +#: taiga/permissions/permissions.py:63 +msgid "View user story" +msgstr "" + +#: taiga/permissions/permissions.py:64 +msgid "Add user story" msgstr "" #: taiga/permissions/permissions.py:65 -msgid "Add task" +msgid "Modify user story" msgstr "" #: taiga/permissions/permissions.py:66 -msgid "Modify task" +msgid "Delete user story" msgstr "" -#: taiga/permissions/permissions.py:67 -msgid "Delete task" +#: taiga/permissions/permissions.py:70 +msgid "Add task" msgstr "" #: taiga/permissions/permissions.py:71 -msgid "Add issue" +msgid "Modify task" msgstr "" #: taiga/permissions/permissions.py:72 +msgid "Delete task" +msgstr "" + +#: taiga/permissions/permissions.py:76 +msgid "Add issue" +msgstr "" + +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "" @@ -1084,8 +1097,8 @@ msgid "Project ID not matches between object and project" msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1094,11 +1107,12 @@ msgstr "" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1115,7 +1129,7 @@ msgstr "" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1132,18 +1146,18 @@ msgstr "" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "" @@ -1173,11 +1187,11 @@ msgid "Multi-Line Text" msgstr "" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "" @@ -1341,23 +1355,23 @@ msgstr "" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "" -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "" -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "" -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "" -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "" @@ -1403,9 +1417,9 @@ msgstr "" msgid "external reference" msgstr "" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "" @@ -1418,8 +1432,8 @@ msgstr "" msgid "estimated finish date" msgstr "" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "" @@ -1448,175 +1462,175 @@ msgstr "" msgid "'project' parameter is mandatory" msgstr "" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "" @@ -1632,28 +1646,33 @@ msgstr "" msgid "Ignoring" msgstr "" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "" @@ -2128,7 +2147,7 @@ msgid "" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" msgstr "" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "" @@ -2152,51 +2171,51 @@ msgstr "" msgid "You can't leave the project if there are no more owners" msgstr "" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "" @@ -2208,15 +2227,15 @@ msgstr "" msgid "Project End" msgstr "" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2576,15 +2595,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2636,24 +2655,24 @@ msgstr "" msgid "There's no task status with that id" msgstr "" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "" @@ -2665,7 +2684,7 @@ msgstr "" msgid "href" msgstr "" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -2681,56 +2700,56 @@ msgstr "" msgid "Important dates" msgstr "" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "" @@ -2807,15 +2826,15 @@ msgstr "" msgid "permissions" msgstr "" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "" -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index e0da36cc..12b13166 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:33+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" @@ -35,11 +35,11 @@ msgstr "Tipo de registro inválido" msgid "invalid login type" msgstr "Tipo de login inválido" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "nombre de usuario no válido" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Son necesarios. 255 caracteres o menos (letras, números y /./-/_)" @@ -345,12 +345,12 @@ msgstr "Error de integridad por argumentos incorrectos o inválidos" msgid "Precondition error" msgstr "Error por incumplimiento de precondición" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Error en los típos de parámetros de filtrado" -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' debe ser un valor entero." @@ -559,24 +559,24 @@ msgstr "error importando las etiquetas" msgid "error importing timelines" msgstr "error importando los timelines" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" no se ha encontrado en este proyecto" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Contenido inválido. Debe ser {\"clave\": \"valor\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" @@ -838,8 +838,9 @@ msgstr "comentario" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "fecha de creación" @@ -908,8 +909,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no es un json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "El proyecto no existe" @@ -1091,12 +1092,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Ver proyecto" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Ver sprints" @@ -1104,167 +1105,179 @@ msgstr "Ver sprints" msgid "View user stories" msgstr "Ver historias de usuarios" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Ver tareas" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Ver peticiones" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Ver páginas del wiki" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Ver enlaces del wiki" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Votar peticiones" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Solicitar afiliación" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Añadir historias de usuario al proyecto" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Agregar comentarios a historia de usuario" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Agregar comentarios a tareas" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Añadir peticiones" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Añadir comentarios a peticiones" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Agregar pagina wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Modificar pagina wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Agregar enlace wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Modificar enlace wiki" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Añadir sprint" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Modificar sprint" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Borrar sprint" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Ver historia de usuario" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Agregar historia de usuario" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Modificar historia de usuario" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Borrar historia de usuario" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Agregar tarea" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Modificar tarea" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Borrar tarea" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Añadir petición" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Modificar petición" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Borrar petición" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Borrar pagina wiki" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Borrar enlace wiki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Modificar proyecto" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Agregar miembro" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Eliminar miembro" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Eliminar proyecto" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Administrar valores de proyecto" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Administrar roles" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Nombre de plantilla invalido" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Descripción de plantilla invalida" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Al menos uno de los usuario debe ser un administrador." -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." @@ -1277,8 +1290,8 @@ msgid "Project ID not matches between object and project" msgstr "El ID de proyecto no coincide entre el adjunto y un proyecto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1287,11 +1300,12 @@ msgstr "Dueño" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1308,7 +1322,7 @@ msgstr "id de objeto" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1325,18 +1339,18 @@ msgstr "está desactualizado" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "descripción" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "orden" @@ -1366,11 +1380,11 @@ msgid "Multi-Line Text" msgstr "Texto multilínea" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "nombre" @@ -1534,23 +1548,23 @@ msgstr "nota de bloqueo" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tienes permisos para asignar un sprint a esta petición." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "No tienes permisos para asignar un estado a esta petición." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "No tienes permisos para establecer la gravedad de esta petición." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "No tienes permiso para establecer la prioridad de esta petición." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "No tienes permiso para establecer el tipo de esta petición." @@ -1596,9 +1610,9 @@ msgstr "asignado a" msgid "external reference" msgstr "referencia externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1611,8 +1625,8 @@ msgstr "fecha estimada de comienzo" msgid "estimated finish date" msgstr "fecha estimada de finalización" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "está cerrada" @@ -1643,175 +1657,175 @@ msgstr "el parámetro '{param}' es obligatório" msgid "'project' parameter is mandatory" msgstr "el parámetro 'project' es obligatório" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "email" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "creado el" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "texto extra de la invitación" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "orden del usuario" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "El usuario ya es miembro del proyecto" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "puntos por defecto" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "estado de historia por defecto" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "estado de tarea por defecto" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "prioridad por defecto" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "gravedad por defecto" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "estado de petición por defecto" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "tipo de petición por defecto" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "miembros" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "total de sprints" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "puntos de historia totales" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "panel de backlog activado" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "panel de kanban activado" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "panel de wiki activo" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "panel de peticiones activo" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "sistema de videoconferencia" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "datos extra de videoconferencia" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "creación de plantilla" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "permisos de anónimo" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "permisos de usuario" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "privado" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "colores de etiquetas" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "configuración de modulos" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "archivado" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "color" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "limite del trabajo en progreso" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "rol por defecto para el propietario" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "opciones por defecto" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "estatuas de historias" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "puntos" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "estatus de tareas" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "estados de petición" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "tipos de petición" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "prioridades" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "gravedades" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "roles" @@ -1827,28 +1841,33 @@ msgstr "Observado" msgid "Ignoring" msgstr "Ignorado" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "observadores" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "fecha y hora de creación" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "fecha y hora de actualización" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "entradas del histórico" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "usuarios notificados" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "" "Ya existe una política de notificación para este usuario en el proyecto." @@ -2560,7 +2579,7 @@ msgstr "" "\n" "[%(project)s] Borrada la página del wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Los observadores tienen usuarios invalidos" @@ -2585,51 +2604,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "No puedes abandonar este proyecto si no existen mas propietarios del mismo" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "La dirección de email ya está en uso." -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rol inválido para el proyecto" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "El número total de sprints debe ser mayor o igual a cero" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Opciones por defecto" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Estados de historia de usuario" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Puntos" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Estado de tareas" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Estados de peticion" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Tipos de petición" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Gravedades" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Roles" @@ -2641,15 +2660,15 @@ msgstr "Sprint futuro" msgid "Project End" msgstr "Final de proyecto" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "No tienes permisos para asignar este sprint a esta tarea." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "No tienes permisos para asignar esta historia a esta tarea." -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "No tienes permisos para asignar este estado a esta tarea." @@ -3061,17 +3080,17 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "No tienes permisos para asignar este sprint a esta historia de usuario." -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" "No tienes permisos para asignar este estado a esta historia de usuario." -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3125,24 +3144,24 @@ msgstr "No existe ningún estado de historia con este id" msgid "There's no task status with that id" msgstr "No existe ningún estado de tarea con este id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Votos" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "votos" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Voto" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "el parámetro 'content' es obligatório" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "el parámetro 'project_id' es obligatório" @@ -3154,7 +3173,7 @@ msgstr "última modificación por" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "Comprueba la API de histórico para obtener el diff exacto" @@ -3170,57 +3189,57 @@ msgstr "Permisos" msgid "Important dates" msgstr "datos importántes" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Nombre de usuario o email no válidos" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "¡Correo enviado con éxito!" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "token inválido" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "La contraseña actual es obligatoria." -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "La nueva contraseña es obligatoria" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "La longitud de la contraseña debe de ser de al menos 6 caracteres" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Contraseña actual inválida" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Argumentos incompletos" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Formato de imagen no válido" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Email duplicado" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Email no válido" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invalido, ¿estás seguro de que el token es correcto y no se ha usado antes?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, ¿estás seguro de que el token es correcto?" @@ -3301,15 +3320,15 @@ msgstr "nueva dirección de email" msgid "permissions" msgstr "permisos" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "no válido" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Nombre de usuario inválido. Prueba con otro." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Nombre de usuario o contraseña inválidos." diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index cd886a04..e9173067 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" @@ -32,11 +32,11 @@ msgstr "väärä rekisterin tyyppi" msgid "invalid login type" msgstr "väärä kirjautumistyyppi" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "tuntematon käyttäjänimi" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" @@ -331,12 +331,12 @@ msgstr "Integrity Error for wrong or invalid arguments" msgid "Precondition error" msgstr "Precondition error" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Error in filter params types." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' must be an integer value." @@ -546,24 +546,24 @@ msgstr "virhe avainsanojen sisäänlukemisessa" msgid "error importing timelines" msgstr "virhe aikajanojen tuonnissa" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" ei löytynyt tästä projektista" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Virheellinen sisältä, pitää olla muodossa {\"avain\": \"arvo\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" @@ -822,8 +822,9 @@ msgstr "kommentti" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "luontipvm" @@ -894,8 +895,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "The payload is not a valid json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Projektia ei löydy" @@ -1054,12 +1055,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Katso projektia" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Katso virstapylvästä" @@ -1067,167 +1068,179 @@ msgstr "Katso virstapylvästä" msgid "View user stories" msgstr "Katso käyttäjätarinoita" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Katso tehtäviä" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Katso pyyntöjä" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Katso wiki-sivuja" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Katso wiki-linkkejä" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Äänestä pyyntöjä" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Pyydä jäsenyyttä" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Lisää käyttäjätarina projektiin" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Lisää kommentteja käyttäjätarinoihin" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Lisää kommentteja tehtäviin" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Lisää pyyntöjä" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Lisää kommentteja pyyntöihin" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Lisää wiki-sivu" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Muokkaa wiki-sivua" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Lisää wiki-linkki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Muokkaa wiki-linkkiä" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Lisää virstapylväs" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Muokkaa virstapyvästä" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Poista virstapylväs" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Katso käyttäjätarinaa" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Lisää käyttäjätarina" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Muokkaa käyttäjätarinaa" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Poista käyttäjätarina" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Lisää tehtävä" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Muokkaa tehtävää" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Poista tehtävä" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Lisää pyyntö" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Muokkaa pyyntöä" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Poista pyyntö" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Poista wiki-sivu" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Poista wiki-linkki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Muokkaa projekti" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Lisää jäsen" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Poista jäsen" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Poista projekti" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Hallinnoi projektin arvoja" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Hallinnoi rooleja" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Virheellinen mallipohjan nimi" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Virheellinen mallipohjan kuvaus" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Vähintään yhden käyttäjän pitää olla aktiivinen ylläpitäjä" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." @@ -1240,8 +1253,8 @@ msgid "Project ID not matches between object and project" msgstr "Projekti ID ei vastaa kohdetta ja projektia" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1250,11 +1263,12 @@ msgstr "omistaja" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1271,7 +1285,7 @@ msgstr "objekti ID" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1288,18 +1302,18 @@ msgstr "on poistettu" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "kuvaus" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "order" @@ -1329,11 +1343,11 @@ msgid "Multi-Line Text" msgstr "" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "nimi" @@ -1497,23 +1511,23 @@ msgstr "suljettu muistiinpano" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "Sinulla ei ole oikeuksia laittaa kierrosta tälle pyynnölle." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "Sinulla ei ole oikeutta asettaa statusta tälle pyyntö." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "Sinulla ei ole oikeutta asettaa vakavuutta tälle pyynnölle." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "Sinulla ei ole oikeutta asettaa kiireellisyyttä tälle pyynnölle." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Sinulla ei ole oikeutta asettaa tyyppiä tälle pyyntö." @@ -1559,9 +1573,9 @@ msgstr "tekijä" msgid "external reference" msgstr "ulkoinen viittaus" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "hukka-aika" @@ -1574,8 +1588,8 @@ msgstr "arvioitu alkupvm" msgid "estimated finish date" msgstr "arvioitu loppupvm" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "on suljettu" @@ -1604,175 +1618,175 @@ msgstr "'{param}' parametri on pakollinen" msgid "'project' parameter is mandatory" msgstr "'project' parametri on pakollinen" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "sähköposti" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "luo täällä" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "tunniste" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "kutsun lisäteksti" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "käyttäjäjärjestys" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "Käyttäjä on jo projektin jäsen" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "oletuspisteet" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "oletus Kt tila" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "oletus tehtävän tila" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "oletus kiireellisyys" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "oletus vakavuus" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "oletus pyynnön tila" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "oletus pyyntö tyyppi" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "jäsenet" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "virstapyväitä yhteensä" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "käyttäjätarinan yhteispisteet" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "aktiivinen odottavien paneeli" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "aktiivinen kanban-paneeli" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "aktiivinen wiki-paneeli" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "aktiivinen pyyntöpaneeli" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "videokokous järjestelmä" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "luo mallipohja" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "vieraan oikeudet" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "käyttäjän oikeudet" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "on yksityinen" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "avainsanojen värit" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "moduulien asetukset" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "on arkistoitu" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "väri" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "työn alla olevien max" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "arvo" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "oletus omistajan rooli" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "oletus optiot" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "kt tilat" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pisteet" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "tehtävän tilat" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "pyyntöjen tilat" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "pyyntötyypit" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "kiireellisyydet" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "vakavuudet" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "roolit" @@ -1788,28 +1802,33 @@ msgstr "Seuraa" msgid "Ignoring" msgstr "Ohittaa" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "vahdit" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "luontipvm" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "päivityspvm" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "historian kohteet" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "ilmoita käyttäjille" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "Ilmoita olemassaolosta määritellyille käyttäjille ja projektille" @@ -2530,7 +2549,7 @@ msgstr "" "\n" "[%(project)s] Poistettiin wiki-sivu \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Vahdit sisältävät virheellisiä käyttäjiä" @@ -2554,51 +2573,51 @@ msgstr "versio" msgid "You can't leave the project if there are no more owners" msgstr "Et voi jättää projektia, jos olet ainoa omistaja" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Sähköpostiosoite on jo käytössä" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Virheellinen rooli projektille" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Virstapylväitä yhteensä pitää olla vähintään 0." -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Oletusoptiot" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Käyttäjätarinatilat" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Pisteet" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Tehtävien tilat" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Pyyntöjen tilat" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "pyyntötyypit" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Kiireellisyydet" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Vakavuudet" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Roolit" @@ -2610,15 +2629,15 @@ msgstr "Tuleva kierros" msgid "Project End" msgstr "Projektin loppu" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -3027,15 +3046,15 @@ msgstr "Tuoteomistaja" msgid "Stakeholder" msgstr "Sidosryhmä" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3089,24 +3108,24 @@ msgstr "En löydä käyttäjätarinan tilaa tällä id:llä" msgid "There's no task status with that id" msgstr "En löydä tehtävän tilaa tällä id:llä" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Ääniä" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "ääniä" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Äänestä" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "'content' parametri on pakollinen" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "'project_id' parametri on pakollinen" @@ -3118,7 +3137,7 @@ msgstr "viimeksi muokannut" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -3134,58 +3153,58 @@ msgstr "Oikeudet" msgid "Important dates" msgstr "Tärkeät päivämäärät" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Tuntematon käyttäjänimi tai sähköposti" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "Sähköposti lähetetty." -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Tunniste on virheellinen" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Nykyinen salasanaparametri tarvitaan" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Uusi salasanaparametri tarvitaan" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Salasanan pitää olla vähintään 6 merkkiä pitkä" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Virheellinen nykyinen salasana" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Puutteelliset argumentit" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Väärä kuvaformaatti" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Sähköposti on jo olemassa" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Virheellinen sähköposti" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Virheellinen. Oletko varma, että tunniste on oikea ja et ole jo käyttänyt " "sitä?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Virheellinen, oletko varma että tunniste on oikea?" @@ -3266,15 +3285,15 @@ msgstr "uusi sähköpostiosoite" msgid "permissions" msgstr "oikeudet" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "virheellinen" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Tuntematon käyttäjänimi, yritä uudelleen." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Käyttäjätunnus tai salasana eivät ole oikein." diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index d7deb7f8..c79bfb32 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -16,8 +16,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" @@ -39,11 +39,11 @@ msgstr "type d'inscription invalide" msgid "invalid login type" msgstr "type d'identifiant invalide" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "nom d'utilisateur invalide" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" @@ -353,12 +353,12 @@ msgstr "Erreur d'intégrité ou arguments invalides" msgid "Precondition error" msgstr "Erreur de précondition" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Erreur dans les types de paramètres de filtres" -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' doit être une valeur entière." @@ -575,24 +575,24 @@ msgstr "erreur lors de l'importation des mots-clés" msgid "error importing timelines" msgstr "erreur lors de l'import des timelines" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" non trouvé dans the projet" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Format non valide. Il doit être de la forme {\"cle\": \"valeur\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" @@ -810,8 +810,9 @@ msgstr "Commentaire" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "Date de création" @@ -880,8 +881,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Le payload n'est pas un json valide" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Le projet n'existe pas" @@ -1025,12 +1026,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Consulter le projet" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Voir les jalons" @@ -1038,167 +1039,179 @@ msgstr "Voir les jalons" msgid "View user stories" msgstr "Voir les histoires utilisateur" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Consulter les tâches" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Voir les problèmes" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Consulter les pages Wiki" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Consulter les liens Wiki" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Voter pour les problèmes" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Demander à devenir membre" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Ajouter l'histoire utilisateur au projet" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Ajouter des commentaires aux histoires utilisateur" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Ajouter des commentaires à une tâche" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Ajouter des problèmes" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Ajouter des commentaires aux problèmes" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Ajouter une page Wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Modifier une page Wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Ajouter un lien Wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Modifier un lien Wiki" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Ajouter un jalon" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Modifier le jalon" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Supprimer le jalon" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Voir l'histoire utilisateur" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Ajouter une histoire utilisateur" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Modifier l'histoire utilisateur" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Supprimer l'histoire utilisateur" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Ajouter une tâche" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Modifier une tâche" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Supprimer une tâche" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Ajouter un problème" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Modifier le problème" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Supprimer le problème" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Supprimer une page Wiki" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Supprimer un lien Wiki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Modifier le projet" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Ajouter un membre" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Supprimer un membre" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Supprimer le projet" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Administrer les paramètres du projet" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Administrer les rôles" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Nom de modèle non valide" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Description du modèle non valide" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Au moins un utilisateur doit être un administrateur actif" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" @@ -1211,8 +1224,8 @@ msgid "Project ID not matches between object and project" msgstr "L'identifiant du projet de correspond pas entre l'objet et le projet" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1221,11 +1234,12 @@ msgstr "propriétaire" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1242,7 +1256,7 @@ msgstr "identifiant de l'objet" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1259,18 +1273,18 @@ msgstr "est obsolète" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "description" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "ordre" @@ -1300,11 +1314,11 @@ msgid "Multi-Line Text" msgstr "Texte multi-ligne" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "nom" @@ -1468,23 +1482,23 @@ msgstr "note bloquée" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "Vous n'avez pas la permission d'affecter ce sprint à ce problème." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "Vous n'avez pas la permission d'affecter ce statut à ce problème." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "Vous n'avez pas la permission d'affecter cette sévérité à ce problème." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "Vous n'avez pas la permission d'affecter cette priorité à ce problème." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Vous n'avez pas la permission d'affecter ce type à ce problème." @@ -1530,9 +1544,9 @@ msgstr "assigné à" msgid "external reference" msgstr "référence externe" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1545,8 +1559,8 @@ msgstr "date de démarrage estimée" msgid "estimated finish date" msgstr "date de fin estimée" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "est fermé" @@ -1575,175 +1589,175 @@ msgstr "'{param}' paramètre obligatoire" msgid "'project' parameter is mandatory" msgstr "'project' paramètre obligatoire" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "email" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "Créé le" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "jeton" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "Text supplémentaire de l'invitation" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "classement utilisateur" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "L'utilisateur est déjà un membre du projet" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "Points par défaut" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "statut de l'HU par défaut" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "Etat par défaut des tâches" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "Priorité par défaut" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "Sévérité par défaut" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "statut du problème par défaut" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "type de problème par défaut" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "membres" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "total des jalons" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "total des points d'histoire" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "panneau backlog actif" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "panneau kanban actif" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "panneau wiki actif" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "panneau problèmes actif" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "plateforme de vidéoconférence" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "Modèle de création" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "Permissions anonymes" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "Permission de l'utilisateur" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "est privé" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "couleurs des tags" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "Configurations des modules" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "est archivé" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "couleur" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "limite de travail en cours" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "valeur" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "rôle par défaut du propriétaire" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "options par défaut" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "statuts des us" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "points" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "états des tâches" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "statuts des problèmes" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "types de problèmes" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "priorités" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "sévérités" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "rôles" @@ -1759,28 +1773,33 @@ msgstr "Sous surveillance" msgid "Ignoring" msgstr "Ignoré" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "observateurs" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "date de création" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "date de mise à jour" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "entrées dans l'historique" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "notifier les utilisateurs" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "La notification existe pour l'utilisateur et le projet spécifiés" @@ -2273,7 +2292,7 @@ msgstr "" "\n" "[%(project)s] Page Wiki \"%(page)s\" supprimée\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "La liste des observateurs contient des utilisateurs invalides" @@ -2298,51 +2317,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "Vous ne pouvez pas quitter le projet si il n'y a plus d'autres propriétaires" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Adresse email déjà existante" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rôle non valide pour le projet" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Le nombre de jalons doit être supérieur ou égal à zéro" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Options par défaut" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Etats de la User Story" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Points" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Etats des tâches" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Statuts des problèmes" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Types de problèmes" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Priorités" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Sévérités" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Rôles" @@ -2354,15 +2373,15 @@ msgstr "Sprint futurs" msgid "Project End" msgstr "Fin du projet" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2759,15 +2778,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Participant" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2821,24 +2840,24 @@ msgstr "Il n'y a pas de statut d'user story avec cet id" msgid "There's no task status with that id" msgstr "Il n'y a pas de statut de tâche avec cet id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Votes" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "votes" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "vote" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "'content' paramètre obligatoire" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "'project_id' paramètre obligatoire" @@ -2850,7 +2869,7 @@ msgstr "dernier modificateur" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -2866,58 +2885,58 @@ msgstr "Permissions" msgid "Important dates" msgstr "Dates importantes" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Nom d'utilisateur ou email non valide" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "Mail envoyé avec succès!" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Jeton invalide" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Paramètre 'mot de passe actuel' requis" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Paramètre 'nouveau mot de passe' requis" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Le mot de passe doit être d'au moins 6 caractères" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Mot de passe actuel incorrect" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "arguments manquants" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "format de l'image non valide" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Email dupliquée" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Email non valide" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invalide, êtes-vous sûre que le jeton est correct et qu'il n'a pas déjà été " "utilisé ?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Invalide, êtes-vous sûre que le jeton est correct ?" @@ -2999,15 +3018,15 @@ msgstr "nouvelle adresse email" msgid "permissions" msgstr "permissions" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "invalide" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Nom d'utilisateur invalide. Essayez avec un autre nom." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Aucun utilisateur avec ce nom ou ce mot de passe." diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index a8265e1c..5571d643 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" @@ -32,11 +32,11 @@ msgstr "ongeldig registratie type" msgid "invalid login type" msgstr "ongeldig login type" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "ongeldige gebruikersnaam" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Verplicht. 255 tekens of minder. Letters, nummers en /./-/_ tekens'" @@ -341,12 +341,12 @@ msgstr "Integriteitsfout voor verkeerde of ongeldige argumenten" msgid "Precondition error" msgstr "Preconditie fout" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Fout in filter params types." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' moet een integer waarde zijn." @@ -559,24 +559,24 @@ msgstr "fout bij importeren tags" msgid "error importing timelines" msgstr "fout bij importeren tijdlijnen" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" niet gevonden in dit project" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Ongeldige inhoud. Volgend formaat geldt {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" @@ -772,8 +772,9 @@ msgstr "commentaar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "aanmaakdatum" @@ -843,8 +844,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "De payload is geen geldige json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Het project bestaat niet" @@ -988,12 +989,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Bekijk project" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Bekijk milestones" @@ -1001,167 +1002,179 @@ msgstr "Bekijk milestones" msgid "View user stories" msgstr "Bekijk user stories" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Bekijk taken" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Bekijk issues" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Bekijk wiki pagina's" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Bekijk wiki links" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Stem op issues" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Vraag lidmaatschap aan" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Voeg user story toe aan project" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Voeg commentaar toe aan user stories" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Voeg commentaar toe aan taken" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Voeg issues toe" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Voeg commentaar toe aan issues" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Voeg wiki pagina toe" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Wijzig wiki pagina" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Voeg wiki link toe" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Wijzig wiki link" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Voeg milestone toe" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Wijzig milestone" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Verwijder milestone" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Bekijk user story" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Voeg user story toe" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Wijzig user story" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Verwijder user story" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Voeg taak toe" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Wijzig taak" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Verwijder taak" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Voeg issue toe" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Wijzig issue" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Verwijder issue" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Verwijder wiki pagina" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Verwijder wiki link" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Wijzig project" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Voeg lid toe" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Verwijder lid" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Verwijder project" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Admin project waarden" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Admin rollen" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Ongeldige template naam" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Ongeldige template omschrijving" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Minstens één van de gebruikers moet een active admin zijn" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." @@ -1174,8 +1187,8 @@ msgid "Project ID not matches between object and project" msgstr "Project ID van object is niet gelijk aan die van het project" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1184,11 +1197,12 @@ msgstr "eigenaar" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1205,7 +1219,7 @@ msgstr "object id" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1222,18 +1236,18 @@ msgstr "is verouderd" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "omschrijving" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "volgorde" @@ -1263,11 +1277,11 @@ msgid "Multi-Line Text" msgstr "" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "naam" @@ -1431,25 +1445,25 @@ msgstr "geblokkeerde notitie" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "Je hebt geen toestemming om deze sprint op deze issue te zetten." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "Je hebt geen toestemming om deze status toe te kennen aan dze issue." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Je hebt geen toestemming om dit ernstniveau toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Je hebt geen toestemming om deze prioriteit toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Je hebt geen toestemming om dit type toe te kennen aan deze issue." @@ -1495,9 +1509,9 @@ msgstr "toegewezen aan" msgid "external reference" msgstr "externe referentie" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1510,8 +1524,8 @@ msgstr "geschatte start datum" msgid "estimated finish date" msgstr "geschatte datum van afwerking" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "is gesloten" @@ -1540,175 +1554,175 @@ msgstr "'{param}' parameter is verplicht" msgid "'project' parameter is mandatory" msgstr "'project' parameter is verplicht" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "e-mail" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "aangemaakt op" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "uitnodiging extra text" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "gebruiker volgorde" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "The gebruikers is al lid van het project" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "standaard punten" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "standaard US status" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "default taak status" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "standaard prioriteit" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "standaard ernstniveau" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "standaard issue status" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "standaard issue type" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "leden" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "totaal van de milestones" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "totaal story points" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "actief backlog paneel" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "actief kanban paneel" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "actief wiki paneel" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "actief issues paneel" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "videoconference systeem" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "aanmaak template" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "anonieme toestemmingen" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "gebruikers toestemmingen" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "is privé" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "tag kleuren" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "module config" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "is gearchiveerd" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "kleur" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "work in progress limiet" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "waarde" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "standaard rol eigenaar" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "standaard instellingen" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "us statussen" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "punten" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "taak statussen" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "issue statussen" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "issue types" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "prioriteiten" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "ernstniveaus" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "rollen" @@ -1724,28 +1738,33 @@ msgstr "Volgend" msgid "Ignoring" msgstr "Negerend" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "volgers" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "aanmaak datum en tijd" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "gewijzigde datum en tijd" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "geschiedenis items" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "verwittig gebruikers" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "Verwittiging bestaat voor gespecifieerde gebruiker en project" @@ -2250,7 +2269,7 @@ msgstr "" "\n" "[%(project)s] Wiki Pagina verwijderd \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Volgers bevat ongeldige gebruikers" @@ -2274,51 +2293,51 @@ msgstr "versie" msgid "You can't leave the project if there are no more owners" msgstr "Je kan het project niet verlaten als er geen andere eigenaars zijn" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "E-mail adres is al in gebruik" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Ongeldige rol voor project" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Totaal milestones moet groter of gelijk zijn aan 0" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Standaard opties" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Status van User story" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Punten" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Statussen van taken" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Statussen van Issues" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Types van issue" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Prioriteiten" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Ernstniveaus" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Rollen" @@ -2330,15 +2349,15 @@ msgstr "Toekomstige sprint" msgid "Project End" msgstr "Project einde" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "" @@ -2722,15 +2741,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2784,24 +2803,24 @@ msgstr "Er is geen user story status met dat id" msgid "There's no task status with that id" msgstr "Er is geen taak status met dat id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Stemmen" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "stemmen" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Stem" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "'inhoud' parameter is verplicht" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "'project_id' parameter is verplicht" @@ -2813,7 +2832,7 @@ msgstr "gebruiker met laatste wijziging" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -2829,56 +2848,56 @@ msgstr "Toestemmingen" msgid "Important dates" msgstr "Belangrijke data" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Ongeldige gebruikersnaam of e-mail" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "Mail met succes verzonden!" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Token is ongeldig" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Huidig wachtwoord parameter vereist" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Nieuw wachtwoord parameter vereist" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Ongeldige lengte van wachtwoord, minstens 6 tekens vereist" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Ongeldig huidig wachtwoord" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Onvolledige argumenten" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Ongeldig afbeelding formaat" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Gedupliceerde e-mail" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Ongeldige e-mail" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "Ongeldig, weet je zeker dat het token correct en ongebruikt is?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Ongeldig, weet je zeker dat het token correct is?" @@ -2959,15 +2978,15 @@ msgstr "nieuw e-mail adres" msgid "permissions" msgstr "toestemmingen" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "ongeldig" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Ongeldige gebruikersnaam. Probeer met een andere." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Gebruikersnaam of wachtwoord stemt niet overeen met gebruiker." diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 80d995d8..aee49354 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -8,9 +8,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-12 18:09+0000\n" -"Last-Translator: Konrad Krawczuk \n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -32,11 +32,11 @@ msgstr "Nieprawidłowy typ rejestracji" msgid "invalid login type" msgstr "Nieprawidłowy typ logowania" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "Nieprawidłowa nazwa użytkownika" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Wymagane. Maksymalnie 255 znaków. Litery, cyfry oraz /./-/_ " @@ -334,12 +334,12 @@ msgstr "Błąd integralności dla błędnych lub nieprawidłowych argumentów" msgid "Precondition error" msgstr "Błąd warunków wstępnych" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Błąd w parametrach typów filtrów." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' musi być wartością typu int." @@ -558,24 +558,24 @@ msgstr "błąd w trakcie importu tagów" msgid "error importing timelines" msgstr "błąd w trakcie importu osi czasu" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" nie odnaleziono w projekcie" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Niewłaściwa zawartość. Musi to być {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Zawiera niewłaściwe pola niestandardowe." -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" @@ -838,8 +838,9 @@ msgstr "komentarz" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "data utworzenia" @@ -909,8 +910,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Źródło nie jest prawidłowym plikiem json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Projekt nie istnieje" @@ -1094,12 +1095,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Zobacz projekt" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Zobacz kamienie milowe" @@ -1107,167 +1108,179 @@ msgstr "Zobacz kamienie milowe" msgid "View user stories" msgstr "Zobacz historyjki użytkownika" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Zobacz zadania" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Zobacz zgłoszenia" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Zobacz strony Wiki" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Zobacz linki Wiki" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Głosuj na zgłoszenia" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Poproś o członkowstwo" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Dodaj historyjkę użytkownika do projektu" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Dodaj komentarze do historyjek użytkownika" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Dodaj komentarze do zadań" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Dodaj zgłoszenia" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Dodaj komentarze do zgłoszeń" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Dodaj strony Wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Modyfikuj stronę Wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Dodaj link do Wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Modyfikuj link do Wiki" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Dodaj kamień milowy" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Modyfikuj Kamień milowy" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Usuń kamień milowy" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Zobacz historyjkę użytkownika" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Dodaj historyjkę użytkownika" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Modyfikuj historyjkę użytkownika" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Usuń historyjkę użytkownika" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Dodaj zadanie" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Modyfikuj zadanie" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Usuń zadanie" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Dodaj zgłoszenie" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Modyfikuj zgłoszenie" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Usuń zgłoszenie" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Usuń stronę Wiki" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Usuń link Wiki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Modyfikuj projekt" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Dodaj członka zespołu" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Usuń członka zespołu" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Usuń projekt" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Administruj wartościami projektu" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Administruj rolami" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Nieprawidłowa nazwa szablonu" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Nieprawidłowy opis szablonu" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Przynajmniej jeden użytkownik musi być aktywnym Administratorem" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Nie masz uprawnień by to zobaczyć." @@ -1280,8 +1293,8 @@ msgid "Project ID not matches between object and project" msgstr "ID nie pasuje pomiędzy obiektem a projektem" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1290,11 +1303,12 @@ msgstr "właściciel" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1311,7 +1325,7 @@ msgstr "id obiektu" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1328,18 +1342,18 @@ msgstr "jest przestarzałe" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "opis" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "kolejność" @@ -1369,11 +1383,11 @@ msgid "Multi-Line Text" msgstr "Teks wielowierszowy" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "nazwa" @@ -1537,23 +1551,23 @@ msgstr "zaglokowana notatka" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "Nie masz uprawnień do połączenia tego zgłoszenia ze sprintem." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "Nie masz uprawnień do ustawienia ważności dla tego zgłoszenia." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "Nie masz uprawnień do ustawienia priorytetu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Nie masz uprawnień do ustawienia typu dla tego zgłoszenia." @@ -1599,9 +1613,9 @@ msgstr "przypisane do" msgid "external reference" msgstr "źródło zgłoszenia" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1614,8 +1628,8 @@ msgstr "szacowana data rozpoczecia" msgid "estimated finish date" msgstr "szacowana data zakończenia" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "jest zamknięte" @@ -1644,175 +1658,175 @@ msgstr "'{param}' parametr jest obowiązkowy" msgid "'project' parameter is mandatory" msgstr "'project' parametr jest obowiązkowy" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "e-mail" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "utwórz na" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "dodatkowy tekst w zaproszeniu" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "kolejność użytkowników" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "Użytkownik już jest członkiem tego projektu" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "domyślne punkty" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "domyślny status dla HU" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "domyślny status dla zadania" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "domyślny priorytet" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "domyślna ważność" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "domyślny status dla zgłoszenia" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "domyślny typ dla zgłoszenia" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "członkowie" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "wszystkich kamieni milowych" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "wszystkich punktów " -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "aktywny panel backlog" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "aktywny panel Kanban" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "aktywny panel Wiki" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "aktywny panel zgłoszeń " -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "system wideokonferencji" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "dodatkowe dane dla wideokonferencji" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "szablon " -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "uprawnienia anonimowych" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "uprawnienia użytkownika" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "jest prywatna" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "kolory tagów" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "konfiguracja modułów" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "zarchiwizowane" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "kolor" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "limit postępu prac" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "wartość" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "domyśla rola właściciela" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "domyślne opcje" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "statusy HU" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pinkty" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "statusy zadań" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "statusy zgłoszeń" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "typy zgłoszeń" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "priorytety" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "ważność" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "role" @@ -1828,28 +1842,33 @@ msgstr "Obserwujesz" msgid "Ignoring" msgstr "Ignorujesz" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "obserwatorzy" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "data utworzenia" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "data aktualizacji" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "wpisy historii" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "powiadom użytkowników" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "Powiadomienie istnieje dla określonego użytkownika i projektu" @@ -2585,7 +2604,7 @@ msgstr "" "\n" "[%(project)s] Usunął stronę Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Obserwatorzy zawierają niepoprawnych użytkowników" @@ -2609,51 +2628,51 @@ msgstr "wersja" msgid "You can't leave the project if there are no more owners" msgstr "Nie możesz opuścić projektu, jeśli jesteś jego jedynym właścicielem" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Tena adres e-mail jest już w użyciu" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Nieprawidłowa rola w projekcie" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Łączna liczba kamieni milowych musi być większa lub równa zero" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Domyślne opcje" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Statusy historyjek użytkownika" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Punkty" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Statusy zadań" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Statusy zgłoszeń" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Typu zgłoszeń" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Priorytety" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Ważność" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Role" @@ -2665,16 +2684,16 @@ msgstr "Przyszły sprint" msgid "Project End" msgstr "Zakończenie projektu" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "Nie masz uprawnień do ustawiania sprintu dla tego zadania." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" "Nie masz uprawnień do ustawiania historyjki użytkownika dla tego zadania" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "Nie masz uprawnień do ustawiania statusu dla tego zadania" @@ -3085,17 +3104,17 @@ msgstr "Właściciel produktu" msgid "Stakeholder" msgstr "Interesariusz" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Nie masz uprawnień do ustawiania sprintu dla tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" "Nie masz uprawnień do ustawiania statusu do tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3149,24 +3168,24 @@ msgstr "Nie ma statusu historyjki użytkownika z takim ID" msgid "There's no task status with that id" msgstr "Nie ma statusu zadania z takim ID" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Głosy" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "głosy" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Głos" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "Parametr 'zawartość' jest wymagany" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "Parametr 'id_projektu' jest wymagany" @@ -3178,7 +3197,7 @@ msgstr "ostatnio zmodyfikowane przez" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -3194,60 +3213,60 @@ msgstr "Uprawnienia" msgid "Important dates" msgstr "Ważne daty" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Nieprawidłowa nazwa użytkownika lub adrs e-mail" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "E-mail wysłany poprawnie!" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Nieprawidłowy token." -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "Należy podać bieżące hasło" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Należy podać nowe hasło" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "" "Nieprawidłowa długość hasła - wymagane jest co najmniej 6 znaków" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Podałeś nieprawidłowe bieżące hasło" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "Pola niekompletne" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Niepoprawny format obrazka" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Zduplikowany adres e-mail" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Niepoprawny adres e-mail" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Niepoprawne, jesteś pewien, że token jest poprawny i nie używałeś go " "wcześniej? " -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Niepoprawne, jesteś pewien, że token jest poprawny?" @@ -3328,15 +3347,15 @@ msgstr "nowy adres e-mail" msgid "permissions" msgstr "uprawnienia" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "Niepoprawne" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Niepoprawna nazwa użytkownika. Spróbuj podać inną." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Nazwa użytkownika lub hasło są nieprawidłowe" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 18724850..09b28df3 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-25 15:53+0000\n" -"Last-Translator: Renato Prado \n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -36,11 +36,11 @@ msgstr "tipo de registro inválido" msgid "invalid login type" msgstr "tipo de login inválido" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "nome de usuário inválido" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Obrigatório. No máximo 255 caracteres. Letras, números e /./-/_ ." @@ -337,12 +337,12 @@ msgstr "Erro de Integridade para argumentos inválidos ou errados" msgid "Precondition error" msgstr "Erro de pré-condição" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Erro nos tipos de parâmetros do filtro." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'projeto' deve ser um valor inteiro." @@ -562,24 +562,24 @@ msgstr "erro importando tags" msgid "error importing timelines" msgstr "erro importando linha do tempo" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" não encontrado nesse projeto" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "conteúdo inválido. Deve ser {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Contém campos personalizados inválidos" -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nome duplicado para o projeto" @@ -841,8 +841,9 @@ msgstr "comentário" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "data de criação" @@ -912,8 +913,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "O carregamento não é um json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "O projeto não existe" @@ -1096,12 +1097,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Ver projeto" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Ver marco de progresso" @@ -1109,167 +1110,179 @@ msgstr "Ver marco de progresso" msgid "View user stories" msgstr "Ver user stories" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Ver tarefa" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Ver casos" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Ver página wiki" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Ver links wiki" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Votar casos" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Solicitar filiação" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Adicionar user story para projeto" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Adicionar comentários para user story" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Adicionar comentário para tarefa" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Adicionar casos" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Adicionar comentários aos casos" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Adicionar página wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "modificar página wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Adicionar link wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Modificar wiki link" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Adicionar marco de progresso" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "modificar marco de progresso" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "remover marco de progresso" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Ver user story" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Adicionar user story" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Modificar user story" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Deletar user story" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Adicionar tarefa" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Modificar tarefa" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Deletar tarefa" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Adicionar caso" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Modificar caso" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Deletar caso" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Deletar página wiki" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Deletar link wiki" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Modificar projeto" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "adicionar membro" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Remover membro" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Deletar projeto" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Valores projeto admin" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Funções Admin" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Nome de template inválido" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Descrição de template inválida" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Pelo menos one dos usuários deve ser um administrador ativo" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "Você não tem permissão para ver isso" @@ -1282,8 +1295,8 @@ msgid "Project ID not matches between object and project" msgstr "Project ID não encontrado entre objeto e projeto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1292,11 +1305,12 @@ msgstr "Dono" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1313,7 +1327,7 @@ msgstr "identidade de objeto" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1330,18 +1344,18 @@ msgstr "está obsoleto" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "descrição" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "ordem" @@ -1371,11 +1385,11 @@ msgid "Multi-Line Text" msgstr "Multi-linha" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "Nome" @@ -1539,23 +1553,23 @@ msgstr "nota bloqueada" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "Você não tem permissão para colocar esse sprint para esse caso." -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "Você não tem permissão para colocar esse status para esse caso." -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "Você não tem permissão para colocar essa severidade para esse caso." -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "Você não tem permissão para colocar essa prioridade para esse caso." -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "Você não tem permissão para colocar esse tipo para esse caso." @@ -1601,9 +1615,9 @@ msgstr "Assinado a" msgid "external reference" msgstr "referência externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "slug" @@ -1616,8 +1630,8 @@ msgstr "Data de início estimada" msgid "estimated finish date" msgstr "data de encerramento estimada" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "está fechado" @@ -1646,175 +1660,175 @@ msgstr "'{param}' parametro é mandatório" msgid "'project' parameter is mandatory" msgstr "'project' parametro é mandatório" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "email" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "criado em" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "token" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "texto extra de convite" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "ordem de usuário" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "O usuário já é membro do projeto" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "pontos padrão" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "status de US padrão" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "status padrão de tarefa" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "prioridade padrão" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "severidade padrão" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "status padrão de caso" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "tipo padrão de caso" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "members" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "total de marcos de progresso" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "pontos totáis de US" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "painel de backlog ativo" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "painel de kanban ativo" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "painel de wiki ativo" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "painel de casos ativo" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "sistema de videoconferencia" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "informação extra de videoconferencia" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "template de criação" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "permissão anônima" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "permissão de usuário" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "é privado" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "cores de tags" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "configurações de modulos" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "está arquivado" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "cor" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "trabalho no limite de progresso" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "função padrão para dono " -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "opções padrão" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "status de US" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pontos" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "status de tarefa" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "status de casos" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "tipos de caso" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "prioridades" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "severidades" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "funções" @@ -1830,28 +1844,33 @@ msgstr "acompanhando" msgid "Ignoring" msgstr "ignorando" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "visualizadores" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "data de criação" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "data de atualização" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "histórico de entradas" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "notificar usuário" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "notificação exist para usuário específico e projeto" @@ -2567,7 +2586,7 @@ msgstr "" "\n" "[%(project)s] Removeu a página Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "Visualizadores contém usuário inválido" @@ -2591,51 +2610,51 @@ msgstr "versão" msgid "You can't leave the project if there are no more owners" msgstr "Você não pode deixar o projeto se não há mais donos" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Endereço de e-mail já utilizado" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Função inválida para projeto" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Total de marcos de progresso devem ser maior ou igual a zero" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Opções padrão" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Status de US" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Pontos" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Status de tarefas" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Status de casos" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Tipos de casos" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Severidades" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Funções" @@ -2647,15 +2666,15 @@ msgstr "Sprint futuro" msgid "Project End" msgstr "Fim do projeto" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "Você não tem permissão para colocar esse sprint para essa tarefa." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "Você não tem permissão para colocar essa user story para essa tarefa." -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "Você não tem permissão para colocar esse status para essa tarefa." @@ -3068,15 +3087,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "Você não tem permissão para colocar esse sprint para essa user story." -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "Você não tem permissão para colocar esse status para essa user story." -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3130,24 +3149,24 @@ msgstr "Não há status de user story com aquele id" msgid "There's no task status with that id" msgstr "Não há status de tarega com aquele id" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Votos" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "votos" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Vote" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "parâmetro 'conteúdo' é mandatório" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "parametro 'project_id' é mandatório" @@ -3159,7 +3178,7 @@ msgstr "último modificador" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "Verifique o histórico da API para a exata diferença" @@ -3175,58 +3194,58 @@ msgstr "Permissões" msgid "Important dates" msgstr "Datas importantes" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "usuário ou e-mail inválido" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "E-mail enviado com sucesso" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "Token é inválido" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "parâmetro de senha atual necessário" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "Parâmetro de nova senha necessário" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "senha atual inválida" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "argumentos incompletos" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "formato de imagem inválida" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "e-mail duplicado" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "Não é um e-mail válido" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Inválido, você está certo que o token está correto e não foi usado " "anteriormente?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, está certo que o token está correto?" @@ -3307,15 +3326,15 @@ msgstr "novo endereço de email" msgid "permissions" msgstr "permissões" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "inválido" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Usuário inválido. Tente com um diferente." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Usuário ou senha não correspondem ao usuário" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 7855a123..16b49b86 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" @@ -35,11 +35,11 @@ msgstr "неправильный тип регистра" msgid "invalid login type" msgstr "неправильный тип логина" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "неправильное имя пользователя" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Обязательно. 255 символов или меньше. Буквы, числа и символы /./-/_ '" @@ -340,12 +340,12 @@ msgstr "Ошибка целостности из-за неправильных msgid "Precondition error" msgstr "Ошибка предусловия" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "Ошибка в типах фильтров для параметров." -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "'project' должно быть целым значением." @@ -565,24 +565,24 @@ msgstr "ошибка импорта тэгов" msgid "error importing timelines" msgstr "ошибка импорта графиков" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" не найдено в этом проекте" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "Неправильные данные. Должны быть в формате {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "Содержит неверные специальные поля" -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" @@ -843,8 +843,9 @@ msgstr "комментарий" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "дата создания" @@ -914,8 +915,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Нагрузочный файл не является правильным json-файлом" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "Проект не существует" @@ -1099,12 +1100,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "Просмотреть проект" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "Просмотреть вехи" @@ -1112,168 +1113,180 @@ msgstr "Просмотреть вехи" msgid "View user stories" msgstr "Просмотреть пользовательские истории" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "Просмотреть задачи" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "Посмотреть проблемы" -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "Просмотреть wiki-страницы" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "Просмотреть wiki-ссылки" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "Проголосовать по проблемам" - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "Запросить членство" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "Добавить пользовательскую историю к проекту" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "Добавить комментарии к пользовательским историям" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "Добавить комментарии к задачам" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "Добавить проблемы" -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "Добавить комментарии к проблемам" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "Создать wiki-страницу" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "Изменить wiki-страницу" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "Добавить wiki-ссылку" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "Изменить wiki-ссылку" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "Добавить веху" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "Изменить веху" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "Удалить веху" -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "Просмотреть пользовательскую историю" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "Добавить пользовательскую историю" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "Изменить пользовательскую историю" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "Удалить пользовательскую историю" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "Добавить задачу" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "Изменить задачу" -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "Удалить задачу" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "Добавить проблему" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "Изменить проблему" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "Удалить проблему" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "Удалить wiki-страницу" -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "Удалить wiki-ссылку" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "Изменить проект" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "Добавить участника" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "Удалить участника" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "Удалить проект" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "Управлять значениями проекта" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "Управлять ролями" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "Неверное название шаблона" -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "Неверное описание шаблона" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "" "По крайней мере один пользователь должен быть активным администратором." -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "У вас нет разрешения на просмотр." @@ -1286,8 +1299,8 @@ msgid "Project ID not matches between object and project" msgstr "Идентификатор проекта не подходит к этому объекту" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1296,11 +1309,12 @@ msgstr "владелец" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1317,7 +1331,7 @@ msgstr "идентификатор объекта" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1334,18 +1348,18 @@ msgstr "устаревшее" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "описание" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "порядок" @@ -1375,11 +1389,11 @@ msgid "Multi-Line Text" msgstr "Многострочный текст" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "имя" @@ -1543,27 +1557,27 @@ msgstr "Заметка о блокировке" msgid "sprint" msgstr "спринт" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "У вас нет прав для того чтобы установить такой спринт для этой проблемы" -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "" "У вас нет прав для того чтобы установить такой статус для этой проблемы" -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "" "У вас нет прав для того чтобы установить такую важность для этой проблемы" -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "" "У вас нет прав для того чтобы установить такой приоритет для этой проблемы" -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "У вас нет прав для того чтобы установить такой тип для этой проблемы" @@ -1609,9 +1623,9 @@ msgstr "назначено" msgid "external reference" msgstr "внешняя ссылка" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "ссылочное имя" @@ -1624,8 +1638,8 @@ msgstr "предполагаемая дата начала" msgid "estimated finish date" msgstr "предполагаемая дата завершения" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "закрыто" @@ -1656,175 +1670,175 @@ msgstr "параметр '{param}' является обязательным" msgid "'project' parameter is mandatory" msgstr "параметр 'project' является обязательным" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "электронная почта" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "создано" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "идентификатор" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "дополнительный текст к приглашению" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "порядок пользователей" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "Этот пользователем уже является участником проекта" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "очки по умолчанию" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "статусы ПИ по умолчанию" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "статус задачи по умолчанию" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "приоритет по умолчанию" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "важность по умолчанию" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "статус проблемы по умолчанию" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "тип проблемы по умолчанию" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "участники" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "общее количество вех" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "очки истории" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "активная панель списка задач" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "активная панель kanban" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "активная wiki-панель" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "активная панель проблем" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "система видеоконференций" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "дополнительные данные системы видеоконференций" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "шаблон для создания" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "права анонимов" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "права пользователя" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "личное" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "цвета тэгов" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "конфигурация модулей" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "архивировано" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "цвет" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "ограничение на активную работу" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "значение" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "роль владельца по умолчанию" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "параметры по умолчанию" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "статусы ПИ" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "очки" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "статусы задач" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "статусы проблем" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "типы проблем" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "приоритеты" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "степени важности" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "роли" @@ -1840,28 +1854,33 @@ msgstr "Отслеживаемое" msgid "Ignoring" msgstr "Игнорируется" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "наблюдатели" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "дата и время создания" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "дата и время обновления" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "записи истории" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "уведомить пользователей" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "Уведомление существует для данных пользователя и проекта" @@ -2583,7 +2602,7 @@ msgstr "" "\n" "[%(project)s] Удалена вики-страница \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "наблюдатели содержат неправильных пользователей" @@ -2607,51 +2626,51 @@ msgstr "версия" msgid "You can't leave the project if there are no more owners" msgstr "Вы не можете покинуть проект если в нём нет других владельцев" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Этот почтовый адрес уже используется" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Неверная роль для этого проекта" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Количество вех должно быть больше или равно нулю" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "Параметры по умолчанию" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "Статусу пользовательских историй" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "Очки" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "Статусы проблемы" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "Типы проблемы" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "Приоритеты" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "Степени важности" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "Роли" @@ -2663,16 +2682,16 @@ msgstr "Будущий спринт" msgid "Project End" msgstr "Окончание проекта" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "У вас нет прав, чтобы назначить этот спринт для этой задачи." -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" "У вас нет прав, чтобы назначить эту историю от пользователя этой задаче." -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "У вас нет прав, чтобы установить этот статус для этой задачи." @@ -3075,17 +3094,17 @@ msgstr "Владелец продукта" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "У вас нет прав чтобы установить спринт для этой пользовательской истории." -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "" "У вас нет прав чтобы установить статус для этой пользовательской истории." -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3137,24 +3156,24 @@ msgstr "Не существует статуса пользовательско msgid "There's no task status with that id" msgstr "Не существует статуса задачи с таким идентификатором" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "Голоса" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "голоса" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "Голосовать" -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "параметр 'content' является обязательным" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "параметр 'project_id' является обязательным" @@ -3166,7 +3185,7 @@ msgstr "" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -3182,56 +3201,56 @@ msgstr "Права доступа" msgid "Important dates" msgstr "" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "Неверное имя пользователя или e-mail" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "Письмо успешно отправлено!" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "Неверная длина пароля, требуется как минимум 6 символов" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "Неверно указан текущий пароль" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "Неправильный формат изображения" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "Этот email уже используется" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "" @@ -3308,15 +3327,15 @@ msgstr "новый email адрес" msgid "permissions" msgstr "" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "Неверное имя пользователя. Попробуйте другое." -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "Имя пользователя или пароль не соответствуют пользователю." diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 87d46ba4..f82efa4b 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-24 10:34+0200\n" -"PO-Revision-Date: 2015-08-08 19:29+0000\n" +"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"PO-Revision-Date: 2015-08-28 08:23+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" @@ -34,11 +34,11 @@ msgstr "無效的註冊類型" msgid "invalid login type" msgstr "無效的登入類型" -#: taiga/auth/serializers.py:34 taiga/users/serializers.py:58 +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 msgid "invalid username" msgstr "無效使用者名稱" -#: taiga/auth/serializers.py:39 taiga/users/serializers.py:64 +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "必填。最多255字元(可為數字,字母,符號....)" @@ -327,12 +327,12 @@ msgstr "因錯誤或無效參數,一致性出錯" msgid "Precondition error" msgstr "前提出錯" -#: taiga/base/filters.py:79 +#: taiga/base/filters.py:80 msgid "Error in filter params types." msgstr "過濾參數類型出錯" -#: taiga/base/filters.py:133 taiga/base/filters.py:222 -#: taiga/base/filters.py:271 +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 msgid "'project' must be an integer value." msgstr "專案須為整數值" @@ -551,24 +551,24 @@ msgstr "滙入標籤出錯" msgid "error importing timelines" msgstr "滙入時間軸出錯" -#: taiga/export_import/serializers.py:161 +#: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" msgstr "{}=\"{}\" 無法在此專案中找到" -#: taiga/export_import/serializers.py:384 +#: taiga/export_import/serializers.py:428 #: taiga/projects/custom_attributes/serializers.py:103 msgid "Invalid content. It must be {\"key\": \"value\",...}" msgstr "無效內容。必須為 {\"key\": \"value\",...}" -#: taiga/export_import/serializers.py:399 +#: taiga/export_import/serializers.py:443 #: taiga/projects/custom_attributes/serializers.py:118 msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" -#: taiga/export_import/serializers.py:468 -#: taiga/projects/milestones/serializers.py:63 taiga/projects/serializers.py:67 -#: taiga/projects/serializers.py:93 taiga/projects/serializers.py:124 -#: taiga/projects/serializers.py:167 +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" @@ -827,8 +827,9 @@ msgstr "評論" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:129 taiga/projects/models.py:561 -#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 msgid "created date" msgstr "創建日期" @@ -895,8 +896,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "載荷為無效json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:137 -#: taiga/projects/tasks/api.py:81 taiga/projects/userstories/api.py:106 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 msgid "The project doesn't exist" msgstr "專案不存在" @@ -1076,12 +1077,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:52 +#: taiga/permissions/permissions.py:55 msgid "View project" msgstr "檢視專案" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:54 +#: taiga/permissions/permissions.py:58 msgid "View milestones" msgstr "檢視里程碑" @@ -1089,167 +1090,179 @@ msgstr "檢視里程碑" msgid "View user stories" msgstr "檢視使用者故事" -#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:69 msgid "View tasks" msgstr "檢視任務 " #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:75 msgid "View issues" msgstr "檢視問題 " -#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:81 msgid "View wiki pages" msgstr "檢視維基頁" -#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:38 -#: taiga/permissions/permissions.py:80 +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:86 msgid "View wiki links" msgstr "檢視維基連結" -#: taiga/permissions/permissions.py:35 taiga/permissions/permissions.py:70 -msgid "Vote issues" -msgstr "票選問題 " - -#: taiga/permissions/permissions.py:39 +#: taiga/permissions/permissions.py:38 msgid "Request membership" msgstr "要求加入會員" -#: taiga/permissions/permissions.py:40 +#: taiga/permissions/permissions.py:39 msgid "Add user story to project" msgstr "專案中新增使用者故事" -#: taiga/permissions/permissions.py:41 +#: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" msgstr "使用者故事附加評論" -#: taiga/permissions/permissions.py:42 +#: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" msgstr "任務附加評論" -#: taiga/permissions/permissions.py:43 +#: taiga/permissions/permissions.py:42 msgid "Add issues" msgstr "加入問題 " -#: taiga/permissions/permissions.py:44 +#: taiga/permissions/permissions.py:43 msgid "Add comments to issues" msgstr "問題加入評論" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" msgstr "新增維基頁" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 msgid "Modify wiki page" msgstr "修改維基頁" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 msgid "Add wiki link" msgstr "新增維基連結" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 msgid "Modify wiki link" msgstr "修改維基連結" -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 +msgid "Star project" +msgstr "" + +#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 +msgid "Vote user story" +msgstr "" + +#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 +msgid "Vote task" +msgstr "" + +#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 +msgid "Vote issue" +msgstr "" + +#: taiga/permissions/permissions.py:59 msgid "Add milestone" msgstr "加入里程碑" -#: taiga/permissions/permissions.py:56 +#: taiga/permissions/permissions.py:60 msgid "Modify milestone" msgstr "修改里程碑" -#: taiga/permissions/permissions.py:57 +#: taiga/permissions/permissions.py:61 msgid "Delete milestone" msgstr "刪除里程碑 " -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:63 msgid "View user story" msgstr "檢視使用者故事" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:64 msgid "Add user story" msgstr "新增使用者故事" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:65 msgid "Modify user story" msgstr "修改使用者故事" -#: taiga/permissions/permissions.py:62 +#: taiga/permissions/permissions.py:66 msgid "Delete user story" msgstr "刪除使用者故事" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:70 msgid "Add task" msgstr "新增任務 " -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:71 msgid "Modify task" msgstr "修改任務 " -#: taiga/permissions/permissions.py:67 +#: taiga/permissions/permissions.py:72 msgid "Delete task" msgstr "刪除任務 " -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:76 msgid "Add issue" msgstr "新增問題 " -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:77 msgid "Modify issue" msgstr "修改問題" -#: taiga/permissions/permissions.py:73 +#: taiga/permissions/permissions.py:78 msgid "Delete issue" msgstr "刪除問題 " -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:84 msgid "Delete wiki page" msgstr "刪除維基頁 " -#: taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:89 msgid "Delete wiki link" msgstr "刪除維基連結" -#: taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:93 msgid "Modify project" msgstr "修改專案" -#: taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:94 msgid "Add member" msgstr "新增成員" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:95 msgid "Remove member" msgstr "移除成員" -#: taiga/permissions/permissions.py:90 +#: taiga/permissions/permissions.py:96 msgid "Delete project" msgstr "刪除專案" -#: taiga/permissions/permissions.py:91 +#: taiga/permissions/permissions.py:97 msgid "Admin project values" msgstr "管理員專案數值" -#: taiga/permissions/permissions.py:92 +#: taiga/permissions/permissions.py:98 msgid "Admin roles" msgstr "管理員角色" -#: taiga/projects/api.py:198 +#: taiga/projects/api.py:176 msgid "Not valid template name" msgstr "非有效樣板名稱 " -#: taiga/projects/api.py:201 +#: taiga/projects/api.py:179 msgid "Not valid template description" msgstr "無效樣板描述" -#: taiga/projects/api.py:469 taiga/projects/serializers.py:261 +#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "至少需有一位使用者擔任管理員" -#: taiga/projects/api.py:499 +#: taiga/projects/api.py:485 msgid "You don't have permisions to see that." msgstr "您無觀看權限" @@ -1262,8 +1275,8 @@ msgid "Project ID not matches between object and project" msgstr "專案ID不符合物件與專案" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:134 -#: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" @@ -1272,11 +1285,12 @@ msgstr "所有者" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:338 taiga/projects/models.py:364 -#: taiga/projects/models.py:395 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41 +#: taiga/projects/models.py:340 taiga/projects/models.py:366 +#: taiga/projects/models.py:397 taiga/projects/models.py:426 +#: taiga/projects/models.py:459 taiga/projects/models.py:482 +#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 msgid "project" @@ -1293,7 +1307,7 @@ msgstr "物件ID" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:132 taiga/projects/models.py:564 +#: taiga/projects/models.py:134 taiga/projects/models.py:566 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1310,18 +1324,18 @@ msgstr "棄用" #: taiga/projects/attachments/models.py:73 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:127 -#: taiga/projects/models.py:559 taiga/projects/tasks/models.py:60 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 +#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 msgid "description" msgstr "描述" #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:354 -#: taiga/projects/models.py:391 taiga/projects/models.py:418 -#: taiga/projects/models.py:453 taiga/projects/models.py:476 -#: taiga/projects/models.py:501 taiga/projects/models.py:534 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 +#: taiga/projects/models.py:393 taiga/projects/models.py:420 +#: taiga/projects/models.py:455 taiga/projects/models.py:478 +#: taiga/projects/models.py:503 taiga/projects/models.py:536 #: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 msgid "order" msgstr "次序" @@ -1351,11 +1365,11 @@ msgid "Multi-Line Text" msgstr "多行列文字" #: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:123 -#: taiga/projects/models.py:350 taiga/projects/models.py:389 -#: taiga/projects/models.py:414 taiga/projects/models.py:451 -#: taiga/projects/models.py:474 taiga/projects/models.py:497 -#: taiga/projects/models.py:532 taiga/projects/models.py:555 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 +#: taiga/projects/models.py:352 taiga/projects/models.py:391 +#: taiga/projects/models.py:416 taiga/projects/models.py:453 +#: taiga/projects/models.py:476 taiga/projects/models.py:499 +#: taiga/projects/models.py:534 taiga/projects/models.py:557 #: taiga/users/models.py:183 taiga/webhooks/models.py:27 msgid "name" msgstr "姓名" @@ -1519,23 +1533,23 @@ msgstr "封鎖筆記" msgid "sprint" msgstr "衝刺任務" -#: taiga/projects/issues/api.py:157 +#: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." msgstr "您無權限設定此問題的衝刺任務" -#: taiga/projects/issues/api.py:161 +#: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." msgstr "您無權限設定此問題的狀態" -#: taiga/projects/issues/api.py:165 +#: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "您無權限設定此問題的嚴重性" -#: taiga/projects/issues/api.py:169 +#: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "您無權限設定此問題的優先性" -#: taiga/projects/issues/api.py:173 +#: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." msgstr "您無權限設定此問題的類型" @@ -1581,9 +1595,9 @@ msgstr "指派給" msgid "external reference" msgstr "外部參考" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:416 -#: taiga/projects/models.py:499 taiga/projects/models.py:557 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 +#: taiga/projects/models.py:354 taiga/projects/models.py:418 +#: taiga/projects/models.py:501 taiga/projects/models.py:559 #: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 msgid "slug" msgstr "代稱" @@ -1596,8 +1610,8 @@ msgstr "预計開始日期" msgid "estimated finish date" msgstr "預計完成日期" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:356 -#: taiga/projects/models.py:420 taiga/projects/models.py:503 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 +#: taiga/projects/models.py:422 taiga/projects/models.py:505 msgid "is closed" msgstr "被關閉" @@ -1626,175 +1640,175 @@ msgstr "'{param}' 參數為必要" msgid "'project' parameter is mandatory" msgstr "'project'參數為必要" -#: taiga/projects/models.py:59 +#: taiga/projects/models.py:61 msgid "email" msgstr "電子郵件" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:63 msgid "create at" msgstr "創建於" -#: taiga/projects/models.py:63 taiga/users/models.py:128 +#: taiga/projects/models.py:65 taiga/users/models.py:128 msgid "token" msgstr "代號" -#: taiga/projects/models.py:69 +#: taiga/projects/models.py:71 msgid "invitation extra text" msgstr "額外文案邀請" -#: taiga/projects/models.py:72 +#: taiga/projects/models.py:74 msgid "user order" msgstr "使用者次序" -#: taiga/projects/models.py:78 +#: taiga/projects/models.py:80 msgid "The user is already member of the project" msgstr "使用者已是專案成員" -#: taiga/projects/models.py:93 +#: taiga/projects/models.py:95 msgid "default points" msgstr "預設點數" -#: taiga/projects/models.py:97 +#: taiga/projects/models.py:99 msgid "default US status" msgstr "預設使用者故事狀態" -#: taiga/projects/models.py:101 +#: taiga/projects/models.py:103 msgid "default task status" msgstr "預設任務狀態" -#: taiga/projects/models.py:104 +#: taiga/projects/models.py:106 msgid "default priority" msgstr "預設優先性" -#: taiga/projects/models.py:107 +#: taiga/projects/models.py:109 msgid "default severity" msgstr "預設嚴重性" -#: taiga/projects/models.py:111 +#: taiga/projects/models.py:113 msgid "default issue status" msgstr "預設問題狀態" -#: taiga/projects/models.py:115 +#: taiga/projects/models.py:117 msgid "default issue type" msgstr "預設議題類型" -#: taiga/projects/models.py:136 +#: taiga/projects/models.py:138 msgid "members" msgstr "成員" -#: taiga/projects/models.py:139 +#: taiga/projects/models.py:141 msgid "total of milestones" msgstr "全部里程碑" -#: taiga/projects/models.py:140 +#: taiga/projects/models.py:142 msgid "total story points" msgstr "全部故事點數" -#: taiga/projects/models.py:143 taiga/projects/models.py:570 +#: taiga/projects/models.py:145 taiga/projects/models.py:572 msgid "active backlog panel" msgstr "活躍的待辦任務優先表面板" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:147 taiga/projects/models.py:574 msgid "active kanban panel" msgstr "活躍的看板式面板" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:149 taiga/projects/models.py:576 msgid "active wiki panel" msgstr "活躍的維基面板" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" msgstr "活躍的問題面板" -#: taiga/projects/models.py:152 taiga/projects/models.py:579 +#: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" msgstr "視訊會議系統" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:156 taiga/projects/models.py:583 msgid "videoconference extra data" msgstr "視訊會議額外資料" -#: taiga/projects/models.py:159 +#: taiga/projects/models.py:161 msgid "creation template" msgstr "創建模版" -#: taiga/projects/models.py:162 +#: taiga/projects/models.py:164 msgid "anonymous permissions" msgstr "匿名權限" -#: taiga/projects/models.py:166 +#: taiga/projects/models.py:168 msgid "user permissions" msgstr "使用者權限" -#: taiga/projects/models.py:169 +#: taiga/projects/models.py:171 msgid "is private" msgstr "私密" -#: taiga/projects/models.py:180 +#: taiga/projects/models.py:182 msgid "tags colors" msgstr "標籤顏色" -#: taiga/projects/models.py:339 +#: taiga/projects/models.py:341 msgid "modules config" msgstr "模組設定" -#: taiga/projects/models.py:358 +#: taiga/projects/models.py:360 msgid "is archived" msgstr "已歸檔" -#: taiga/projects/models.py:360 taiga/projects/models.py:422 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:505 taiga/projects/models.py:536 +#: taiga/projects/models.py:362 taiga/projects/models.py:424 +#: taiga/projects/models.py:457 taiga/projects/models.py:480 +#: taiga/projects/models.py:507 taiga/projects/models.py:538 #: taiga/users/models.py:113 msgid "color" msgstr "顏色" -#: taiga/projects/models.py:362 +#: taiga/projects/models.py:364 msgid "work in progress limit" msgstr "工作進度限制" -#: taiga/projects/models.py:393 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 msgid "value" msgstr "價值" -#: taiga/projects/models.py:567 +#: taiga/projects/models.py:569 msgid "default owner's role" msgstr "預設所有者角色" -#: taiga/projects/models.py:583 +#: taiga/projects/models.py:585 msgid "default options" msgstr "預設選項" -#: taiga/projects/models.py:584 +#: taiga/projects/models.py:586 msgid "us statuses" msgstr "我們狀況" -#: taiga/projects/models.py:585 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "點數" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:588 msgid "task statuses" msgstr "任務狀況" -#: taiga/projects/models.py:587 +#: taiga/projects/models.py:589 msgid "issue statuses" msgstr "問題狀況" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:590 msgid "issue types" msgstr "問題類型" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:591 msgid "priorities" msgstr "優先性" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:592 msgid "severities" msgstr "嚴重性" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:593 msgid "roles" msgstr "角色" @@ -1810,28 +1824,33 @@ msgstr "觀看中" msgid "Ignoring" msgstr "忽視" -#: taiga/projects/notifications/mixins.py:87 -msgid "watchers" -msgstr "觀看者" - -#: taiga/projects/notifications/models.py:59 +#: taiga/projects/notifications/models.py:61 msgid "created date time" msgstr "創建日期時間" -#: taiga/projects/notifications/models.py:61 +#: taiga/projects/notifications/models.py:63 msgid "updated date time" msgstr "更新日期時間" -#: taiga/projects/notifications/models.py:63 +#: taiga/projects/notifications/models.py:65 msgid "history entries" msgstr "歷史輸入" -#: taiga/projects/notifications/models.py:66 +#: taiga/projects/notifications/models.py:68 msgid "notify users" msgstr "通知用戶" -#: taiga/projects/notifications/services.py:63 -#: taiga/projects/notifications/services.py:77 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "" + +#: taiga/projects/notifications/services.py:64 +#: taiga/projects/notifications/services.py:78 msgid "Notify exists for specified user and project" msgstr "通知特定使用者與專案退出" @@ -2556,7 +2575,7 @@ msgstr "" "\n" "[%(project)s] 刪除維基頁 \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:44 +#: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" msgstr "監督者包含無效使用者" @@ -2580,51 +2599,51 @@ msgstr "版本" msgid "You can't leave the project if there are no more owners" msgstr "如果專案無所有者,你將無法脫離該專案" -#: taiga/projects/serializers.py:237 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "電子郵件已使用" -#: taiga/projects/serializers.py:249 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "專案無效的角色" -#: taiga/projects/serializers.py:348 +#: taiga/projects/serializers.py:346 msgid "Total milestones must be major or equal to zero" msgstr "Kanban" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Default options" msgstr "預設選項" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "User story's statuses" msgstr "使用者故事狀態" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Points" msgstr "點數" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:406 msgid "Task's statuses" msgstr "任務狀態" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:407 msgid "Issue's statuses" msgstr "問題狀態" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:408 msgid "Issue's types" msgstr "問題類型" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:409 msgid "Priorities" msgstr "優先性" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:410 msgid "Severities" msgstr "嚴重性" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:411 msgid "Roles" msgstr "角色" @@ -2636,15 +2655,15 @@ msgstr "未來之衝刺" msgid "Project End" msgstr "專案結束" -#: taiga/projects/tasks/api.py:97 taiga/projects/tasks/api.py:106 +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." msgstr "無權限更動此任務下的衝刺任務" -#: taiga/projects/tasks/api.py:100 +#: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "無權限更動此務下的使用者故事" -#: taiga/projects/tasks/api.py:103 +#: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." msgstr "無權限更動此任務下的狀態" @@ -3046,15 +3065,15 @@ msgstr "產品所有人" msgid "Stakeholder" msgstr "利害關係人" -#: taiga/projects/userstories/api.py:153 +#: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." msgstr "無權限更動使用者故事的衝刺任務" -#: taiga/projects/userstories/api.py:157 +#: taiga/projects/userstories/api.py:159 msgid "You don't have permissions to set this status to this user story." msgstr "無權限更動此使用者故事的狀態" -#: taiga/projects/userstories/api.py:251 +#: taiga/projects/userstories/api.py:259 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3107,24 +3126,24 @@ msgstr "該ID無相關使用者故事狀態" msgid "There's no task status with that id" msgstr "該ID無相關任務狀況" +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 -#: taiga/projects/votes/models.py:54 +#: taiga/projects/votes/models.py:56 msgid "Votes" msgstr "投票數" -#: taiga/projects/votes/models.py:50 -msgid "votes" -msgstr "投票數" - -#: taiga/projects/votes/models.py:53 +#: taiga/projects/votes/models.py:55 msgid "Vote" msgstr "投票 " -#: taiga/projects/wiki/api.py:60 +#: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" msgstr "'content'參數為必要" -#: taiga/projects/wiki/api.py:63 +#: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" msgstr "'project_id'參數為必要" @@ -3136,7 +3155,7 @@ msgstr "上次更改" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:88 +#: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" msgstr "" @@ -3152,56 +3171,56 @@ msgstr "許可" msgid "Important dates" msgstr "重要日期" -#: taiga/users/api.py:124 taiga/users/api.py:131 +#: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" msgstr "無效使用者或郵件" -#: taiga/users/api.py:140 +#: taiga/users/api.py:167 msgid "Mail sended successful!" msgstr "成功送出郵件" -#: taiga/users/api.py:152 taiga/users/api.py:157 +#: taiga/users/api.py:179 taiga/users/api.py:184 msgid "Token is invalid" msgstr "代號無效" -#: taiga/users/api.py:178 +#: taiga/users/api.py:205 msgid "Current password parameter needed" msgstr "需要目前密碼之參數" -#: taiga/users/api.py:181 +#: taiga/users/api.py:208 msgid "New password parameter needed" msgstr "需要新密碼參數" -#: taiga/users/api.py:184 +#: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" msgstr "無效密碼長度,至少需6個字元" -#: taiga/users/api.py:187 +#: taiga/users/api.py:214 msgid "Invalid current password" msgstr "無效密碼" -#: taiga/users/api.py:203 +#: taiga/users/api.py:230 msgid "Incomplete arguments" msgstr "不完整參數" -#: taiga/users/api.py:208 +#: taiga/users/api.py:235 msgid "Invalid image format" msgstr "無效的圖片檔案" -#: taiga/users/api.py:261 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "複製電子郵件" -#: taiga/users/api.py:263 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "非有效電子郵性" -#: taiga/users/api.py:283 taiga/users/api.py:289 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "無效,請確認代號正確,之前是否曾使用過?" -#: taiga/users/api.py:316 taiga/users/api.py:324 taiga/users/api.py:327 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "無效,請確認代號是否正確?" @@ -3278,15 +3297,15 @@ msgstr "新電子郵件地址" msgid "permissions" msgstr "許可" -#: taiga/users/serializers.py:59 +#: taiga/users/serializers.py:62 msgid "invalid" msgstr "無效" -#: taiga/users/serializers.py:70 +#: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." msgstr "無效使用者名稱,請重試其它名稱 " -#: taiga/users/services.py:48 taiga/users/services.py:52 +#: taiga/users/services.py:49 taiga/users/services.py:53 msgid "Username or password does not matches user." msgstr "用戶名稱與密碼不符" From 1b5775e15e61ec0aab4b6b67d0bfa07b36c07e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2015 11:02:47 +0200 Subject: [PATCH 107/190] Add 'taiga-info has-closed-milestones' to Access-Control-Expose-Headers --- taiga/base/middleware/cors.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/taiga/base/middleware/cors.py b/taiga/base/middleware/cors.py index 86d6b5c5..3bc8bfac 100644 --- a/taiga/base/middleware/cors.py +++ b/taiga/base/middleware/cors.py @@ -28,13 +28,15 @@ COORS_EXPOSE_HEADERS = ["x-pagination-count", "x-paginated", "x-paginated-by", "x-pagination-current", "x-pagination-next", "x-pagination-prev", "x-site-host", "x-site-register"] +TAIGA_EXPOSE_HEADERS = ["taiga-info-has-closed-milestones"] + class CoorsMiddleware(object): def _populate_response(self, response): response["Access-Control-Allow-Origin"] = COORS_ALLOWED_ORIGINS response["Access-Control-Allow-Methods"] = ",".join(COORS_ALLOWED_METHODS) response["Access-Control-Allow-Headers"] = ",".join(COORS_ALLOWED_HEADERS) - response["Access-Control-Expose-Headers"] = ",".join(COORS_EXPOSE_HEADERS) + response["Access-Control-Expose-Headers"] = ",".join(COORS_EXPOSE_HEADERS + TAIGA_EXPOSE_HEADERS) response["Access-Control-Max-Age"] = "3600" if COORS_ALLOWED_CREDENTIALS: From ca7020e9b30acc13312035cafc0d31f10425a228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2015 12:04:56 +0200 Subject: [PATCH 108/190] Add 'taiga-info has-closed-milestones' in the headers of the call to api/v1/userstories/bulk_update_backlog_order --- taiga/projects/userstories/api.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 802e3e73..a3737488 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -228,9 +228,10 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi field=order_field) services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user) - if order_field == "sprint_order": - # NOTE: This is useful according to issue #2851 to update sprints column in the - # browser client when move USs from the backlog to an sprint or between sprints. + if order_field in ["sprint_order", "backlog_order"]: + # NOTE: This is useful according to issue #2851 to update sprints column in + # the browser client when move USs from the backlog to an sprint, from + # an sprint to the backlog or between sprints. has_closed_milestones = project.milestones.filter(closed=True).exists() self.headers["Taiga-Info-Has-Closed-Milestones"] = has_closed_milestones From 260e5339c2d5ef84e3d5fa1dd4b164816705b152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2015 18:59:56 +0200 Subject: [PATCH 109/190] Fix tests --- tests/integration/test_userstories.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 6b49568a..1eaea1b1 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -156,7 +156,8 @@ def test_api_update_orders_in_bulk_to_test_extra_headers(client): response2 = client.json.post(url2, json.dumps(data)) response3 = client.json.post(url3, json.dumps(data)) assert response1.status_code == 204 - assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == True + assert response1["taiga-info-has-closed-milestones"] == "False" assert response2.status_code == 204 assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False assert response3.status_code == 204 @@ -170,7 +171,8 @@ def test_api_update_orders_in_bulk_to_test_extra_headers(client): response2 = client.json.post(url2, json.dumps(data)) response3 = client.json.post(url3, json.dumps(data)) assert response1.status_code == 204 - assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == False + assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == True + assert response3["taiga-info-has-closed-milestones"] == "True" assert response2.status_code == 204 assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False assert response3.status_code == 204 From b5fed8e65b783e9ce8cfdfd7b06de0e40b896dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2015 18:02:44 +0200 Subject: [PATCH 110/190] Change 'star/unstar project' to 'like/unlike project' --- taiga/permissions/permissions.py | 8 ----- taiga/projects/api.py | 4 +-- .../migrations/0025_auto_20150901_1600.py | 21 ++++++++++++ taiga/projects/permissions.py | 4 +-- taiga/projects/serializers.py | 8 ++--- taiga/projects/votes/mixins/serializers.py | 6 ++-- taiga/projects/votes/mixins/viewsets.py | 12 +++---- taiga/users/api.py | 1 - .../migrations/0013_auto_20150901_1600.py | 21 ++++++++++++ taiga/users/permissions.py | 1 - .../test_projects_resource.py | 16 ++++----- tests/integration/test_star_projects.py | 34 +++++++++---------- 12 files changed, 84 insertions(+), 52 deletions(-) create mode 100644 taiga/projects/migrations/0025_auto_20150901_1600.py create mode 100644 taiga/users/migrations/0013_auto_20150901_1600.py diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index e711c77a..37cd32de 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -45,15 +45,10 @@ USER_PERMISSIONS = [ ('modify_wiki_page', _('Modify wiki page')), ('add_wiki_link', _('Add wiki link')), ('modify_wiki_link', _('Modify wiki link')), - ('star_project', _('Star project')), - ('vote_us', _('Vote user story')), - ('vote_task', _('Vote task')), - ('vote_issue', _('Vote issue')), ] MEMBERS_PERMISSIONS = [ ('view_project', _('View project')), - ('star_project', _('Star project')), # Milestone permissions ('view_milestones', _('View milestones')), ('add_milestone', _('Add milestone')), @@ -64,19 +59,16 @@ MEMBERS_PERMISSIONS = [ ('add_us', _('Add user story')), ('modify_us', _('Modify user story')), ('delete_us', _('Delete user story')), - ('vote_us', _('Vote user story')), # Task permissions ('view_tasks', _('View tasks')), ('add_task', _('Add task')), ('modify_task', _('Modify task')), ('delete_task', _('Delete task')), - ('vote_task', _('Vote task')), # Issue permissions ('view_issues', _('View issues')), ('add_issue', _('Add issue')), ('modify_issue', _('Modify issue')), ('delete_issue', _('Delete issue')), - ('vote_issue', _('Vote issue')), # Wiki page permissions ('view_wiki_pages', _('View wiki pages')), ('add_wiki_page', _('Add wiki page')), diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 43f78475..baaa0eb0 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -45,13 +45,13 @@ from . import models from . import permissions from . import services -from .votes.mixins.viewsets import StarredResourceMixin, VotersViewSetMixin +from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin ###################################################### ## Project ###################################################### -class ProjectViewSet(StarredResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): +class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer admin_serializer_class = serializers.ProjectDetailAdminSerializer diff --git a/taiga/projects/migrations/0025_auto_20150901_1600.py b/taiga/projects/migrations/0025_auto_20150901_1600.py new file mode 100644 index 00000000..8859b14e --- /dev/null +++ b/taiga/projects/migrations/0025_auto_20150901_1600.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import djorm_pgarray.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0024_auto_20150810_1247'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='public_permissions', + field=djorm_pgarray.fields.TextArrayField(default=[], choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', verbose_name='user permissions'), + preserve_default=True, + ), + ] diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 1ec6f984..140faddc 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -60,8 +60,8 @@ class ProjectPermission(TaigaResourcePermission): regenerate_tasks_csv_uuid_perms = IsProjectOwner() tags_perms = HasProjectPerm('view_project') tags_colors_perms = HasProjectPerm('view_project') - star_perms = IsAuthenticated() & HasProjectPerm('view_project') - unstar_perms = IsAuthenticated() & HasProjectPerm('view_project') + like_perms = IsAuthenticated() & HasProjectPerm('view_project') + unlike_perms = IsAuthenticated() & HasProjectPerm('view_project') watch_perms = IsAuthenticated() & HasProjectPerm('view_project') unwatch_perms = IsAuthenticated() & HasProjectPerm('view_project') create_template_perms = IsSuperUser() diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 9a13c6e7..c4f5f506 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -43,7 +43,7 @@ from .custom_attributes.serializers import UserStoryCustomAttributeSerializer from .custom_attributes.serializers import TaskCustomAttributeSerializer from .custom_attributes.serializers import IssueCustomAttributeSerializer from .notifications.mixins import WatchedResourceModelSerializer -from .votes.mixins.serializers import StarredResourceSerializerMixin +from .votes.mixins.serializers import LikedResourceSerializerMixin ###################################################### ## Custom values for selectors @@ -308,7 +308,7 @@ class ProjectMemberSerializer(serializers.ModelSerializer): ## Projects ###################################################### -class ProjectSerializer(WatchersValidator, StarredResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): +class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) anon_permissions = PgArrayField(required=False) public_permissions = PgArrayField(required=False) @@ -385,10 +385,10 @@ class ProjectDetailAdminSerializer(ProjectDetailSerializer): ###################################################### -## Starred +## Liked ###################################################### -class StarredSerializer(serializers.ModelSerializer): +class LikedSerializer(serializers.ModelSerializer): class Meta: model = models.Project fields = ['id', 'name', 'slug'] diff --git a/taiga/projects/votes/mixins/serializers.py b/taiga/projects/votes/mixins/serializers.py index 96028eaf..a9c95900 100644 --- a/taiga/projects/votes/mixins/serializers.py +++ b/taiga/projects/votes/mixins/serializers.py @@ -27,9 +27,9 @@ class BaseVotedResourceSerializer(serializers.ModelSerializer): return getattr(obj, "is_voted", False) or False -class StarredResourceSerializerMixin(BaseVotedResourceSerializer): - stars = serializers.SerializerMethodField("get_votes_counter") - is_starred = serializers.SerializerMethodField("get_is_voted") +class LikedResourceSerializerMixin(BaseVotedResourceSerializer): + likes = serializers.SerializerMethodField("get_votes_counter") + is_liked = serializers.SerializerMethodField("get_is_voted") class VotedResourceSerializerMixin(BaseVotedResourceSerializer): diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py index 83bf25bd..3fbb5cdf 100644 --- a/taiga/projects/votes/mixins/viewsets.py +++ b/taiga/projects/votes/mixins/viewsets.py @@ -55,16 +55,16 @@ class BaseVotedResource: return response.Ok() -class StarredResourceMixin(BaseVotedResource): - # Note: objects nedd 'star' and 'unstar' permissions. +class LikedResourceMixin(BaseVotedResource): + # Note: objects nedd 'like' and 'unlike' permissions. @detail_route(methods=["POST"]) - def star(self, request, pk=None): - return self._add_voter("star", request, pk) + def like(self, request, pk=None): + return self._add_voter("like", request, pk) @detail_route(methods=["POST"]) - def unstar(self, request, pk=None): - return self._remove_vote("unstar", request, pk) + def unlike(self, request, pk=None): + return self._remove_vote("unlike", request, pk) class VotedResourceMixin(BaseVotedResource): diff --git a/taiga/users/api.py b/taiga/users/api.py index ea5e5bdc..508e707f 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -34,7 +34,6 @@ from taiga.base.filters import PermissionBasedFilterBackend from taiga.base.api.utils import get_object_or_404 from taiga.base.filters import MembersFilterBackend from taiga.projects.votes import services as votes_service -from taiga.projects.serializers import StarredSerializer from easy_thumbnails.source_generators import pil_image diff --git a/taiga/users/migrations/0013_auto_20150901_1600.py b/taiga/users/migrations/0013_auto_20150901_1600.py new file mode 100644 index 00000000..8d2e4143 --- /dev/null +++ b/taiga/users/migrations/0013_auto_20150901_1600.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import djorm_pgarray.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0012_auto_20150812_1142'), + ] + + operations = [ + migrations.AlterField( + model_name='role', + name='permissions', + field=djorm_pgarray.fields.TextArrayField(default=[], choices=[('view_project', 'View project'), ('view_milestones', 'View milestones'), ('add_milestone', 'Add milestone'), ('modify_milestone', 'Modify milestone'), ('delete_milestone', 'Delete milestone'), ('view_us', 'View user story'), ('add_us', 'Add user story'), ('modify_us', 'Modify user story'), ('delete_us', 'Delete user story'), ('view_tasks', 'View tasks'), ('add_task', 'Add task'), ('modify_task', 'Modify task'), ('delete_task', 'Delete task'), ('view_issues', 'View issues'), ('add_issue', 'Add issue'), ('modify_issue', 'Modify issue'), ('delete_issue', 'Delete issue'), ('view_wiki_pages', 'View wiki pages'), ('add_wiki_page', 'Add wiki page'), ('modify_wiki_page', 'Modify wiki page'), ('delete_wiki_page', 'Delete wiki page'), ('view_wiki_links', 'View wiki links'), ('add_wiki_link', 'Add wiki link'), ('modify_wiki_link', 'Modify wiki link'), ('delete_wiki_link', 'Delete wiki link')], dbtype='text', verbose_name='permissions'), + preserve_default=True, + ), + ] diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index 63c54751..dab7fe0f 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -44,7 +44,6 @@ class UserPermission(TaigaResourcePermission): change_avatar_perms = IsAuthenticated() me_perms = IsAuthenticated() remove_avatar_perms = IsAuthenticated() - starred_perms = AllowAny() change_email_perms = AllowAny() contacts_perms = AllowAny() favourites_perms = AllowAny() diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 27c08d1f..c76fa68f 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -225,10 +225,10 @@ def test_project_action_issues_stats(client, data): assert results == [404, 404, 200, 200] -def test_project_action_star(client, data): - public_url = reverse('projects-star', kwargs={"pk": data.public_project.pk}) - private1_url = reverse('projects-star', kwargs={"pk": data.private_project1.pk}) - private2_url = reverse('projects-star', kwargs={"pk": data.private_project2.pk}) +def test_project_action_like(client, data): + public_url = reverse('projects-like', kwargs={"pk": data.public_project.pk}) + private1_url = reverse('projects-like', kwargs={"pk": data.private_project1.pk}) + private2_url = reverse('projects-like', kwargs={"pk": data.private_project2.pk}) users = [ None, @@ -244,10 +244,10 @@ def test_project_action_star(client, data): assert results == [404, 404, 200, 200] -def test_project_action_unstar(client, data): - public_url = reverse('projects-unstar', kwargs={"pk": data.public_project.pk}) - private1_url = reverse('projects-unstar', kwargs={"pk": data.private_project1.pk}) - private2_url = reverse('projects-unstar', kwargs={"pk": data.private_project2.pk}) +def test_project_action_unlike(client, data): + public_url = reverse('projects-unlike', kwargs={"pk": data.public_project.pk}) + private1_url = reverse('projects-unlike', kwargs={"pk": data.private_project1.pk}) + private2_url = reverse('projects-unlike', kwargs={"pk": data.private_project2.pk}) users = [ None, diff --git a/tests/integration/test_star_projects.py b/tests/integration/test_star_projects.py index ad8d8e08..2f2b87aa 100644 --- a/tests/integration/test_star_projects.py +++ b/tests/integration/test_star_projects.py @@ -23,11 +23,11 @@ from .. import factories as f pytestmark = pytest.mark.django_db -def test_star_project(client): +def test_like_project(client): user = f.UserFactory.create() project = f.create_project(owner=user) f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-star", args=(project.id,)) + url = reverse("projects-like", args=(project.id,)) client.login(user) response = client.post(url) @@ -35,11 +35,11 @@ def test_star_project(client): assert response.status_code == 200 -def test_unstar_project(client): +def test_unlike_project(client): user = f.UserFactory.create() project = f.create_project(owner=user) f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-unstar", args=(project.id,)) + url = reverse("projects-unlike", args=(project.id,)) client.login(user) response = client.post(url) @@ -75,7 +75,7 @@ def test_get_project_fan(client): assert response.data['id'] == vote.user.id -def test_get_project_stars(client): +def test_get_project_likes(client): user = f.UserFactory.create() project = f.create_project(owner=user) f.MembershipFactory.create(project=project, user=user, is_owner=True) @@ -87,37 +87,37 @@ def test_get_project_stars(client): response = client.get(url) assert response.status_code == 200 - assert response.data['stars'] == 5 + assert response.data['likes'] == 5 -def test_get_project_is_starred(client): +def test_get_project_is_liked(client): user = f.UserFactory.create() project = f.create_project(owner=user) f.MembershipFactory.create(project=project, user=user, is_owner=True) f.VotesFactory.create(content_object=project) url_detail = reverse("projects-detail", args=(project.id,)) - url_star = reverse("projects-star", args=(project.id,)) - url_unstar = reverse("projects-unstar", args=(project.id,)) + url_like = reverse("projects-like", args=(project.id,)) + url_unlike = reverse("projects-unlike", args=(project.id,)) client.login(user) response = client.get(url_detail) assert response.status_code == 200 - assert response.data['stars'] == 0 - assert response.data['is_starred'] == False + assert response.data['likes'] == 0 + assert response.data['is_liked'] == False - response = client.post(url_star) + response = client.post(url_like) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['stars'] == 1 - assert response.data['is_starred'] == True + assert response.data['likes'] == 1 + assert response.data['is_liked'] == True - response = client.post(url_unstar) + response = client.post(url_unlike) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['stars'] == 0 - assert response.data['is_starred'] == False + assert response.data['likes'] == 0 + assert response.data['is_liked'] == False From 70901fd28b56a4d01dcdc887649070b606624b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 1 Sep 2015 14:55:27 +0200 Subject: [PATCH 111/190] Remove 'taiga-info-has-closed-milestones' from userstories resource and added 'taiga-info-total-opened-milestones' and 'taiga-info-total-closed-milestones' to milestones resource --- settings/common.py | 5 +++ taiga/base/middleware/cors.py | 5 +-- taiga/base/utils/db.py | 18 +++++++++++ taiga/projects/milestones/api.py | 24 +++++++++++++- taiga/projects/userstories/api.py | 7 ----- tests/integration/test_milestones.py | 33 ++++++++++++++++++++ tests/integration/test_userstories.py | 45 --------------------------- 7 files changed, 82 insertions(+), 55 deletions(-) diff --git a/settings/common.py b/settings/common.py index e9715241..c32f5cdb 100644 --- a/settings/common.py +++ b/settings/common.py @@ -403,6 +403,11 @@ REST_FRAMEWORK = { "DATETIME_FORMAT": "%Y-%m-%dT%H:%M:%S%z" } +# Extra expose header related to Taiga APP (see taiga.base.middleware.cors=) +APP_EXTRA_EXPOSE_HEADERS = [ + "taiga-info-total-opened-milestones", + "taiga-info-total-closed-milestones" +] DEFAULT_PROJECT_TEMPLATE = "scrum" PUBLIC_REGISTER_ENABLED = False diff --git a/taiga/base/middleware/cors.py b/taiga/base/middleware/cors.py index 3bc8bfac..3d6aed10 100644 --- a/taiga/base/middleware/cors.py +++ b/taiga/base/middleware/cors.py @@ -15,6 +15,7 @@ # along with this program. If not, see . from django import http +from django.conf import settings COORS_ALLOWED_ORIGINS = "*" @@ -28,7 +29,7 @@ COORS_EXPOSE_HEADERS = ["x-pagination-count", "x-paginated", "x-paginated-by", "x-pagination-current", "x-pagination-next", "x-pagination-prev", "x-site-host", "x-site-register"] -TAIGA_EXPOSE_HEADERS = ["taiga-info-has-closed-milestones"] +COORS_EXTRA_EXPOSE_HEADERS = getattr(settings, "APP_EXTRA_EXPOSE_HEADERS", []) class CoorsMiddleware(object): @@ -36,7 +37,7 @@ class CoorsMiddleware(object): response["Access-Control-Allow-Origin"] = COORS_ALLOWED_ORIGINS response["Access-Control-Allow-Methods"] = ",".join(COORS_ALLOWED_METHODS) response["Access-Control-Allow-Headers"] = ",".join(COORS_ALLOWED_HEADERS) - response["Access-Control-Expose-Headers"] = ",".join(COORS_EXPOSE_HEADERS + TAIGA_EXPOSE_HEADERS) + response["Access-Control-Expose-Headers"] = ",".join(COORS_EXPOSE_HEADERS + COORS_EXTRA_EXPOSE_HEADERS) response["Access-Control-Max-Age"] = "3600" if COORS_ALLOWED_CREDENTIALS: diff --git a/taiga/base/utils/db.py b/taiga/base/utils/db.py index a9663751..40afea81 100644 --- a/taiga/base/utils/db.py +++ b/taiga/base/utils/db.py @@ -16,10 +16,28 @@ from django.contrib.contenttypes.models import ContentType from django.db import transaction +from django.shortcuts import _get_queryset from . import functions +def get_object_or_none(klass, *args, **kwargs): + """ + Uses get() to return an object, or None if the object does not exist. + + klass may be a Model, Manager, or QuerySet object. All other passed + arguments and keyword arguments are used in the get() query. + + Note: Like with get(), an MultipleObjectsReturned will be raised if more + than one object is found. + """ + queryset = _get_queryset(klass) + try: + return queryset.get(*args, **kwargs) + except queryset.model.DoesNotExist: + return None + + def get_typename_for_model_class(model:object, for_concrete_model=True) -> str: """ Get typename for model instance. diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py index 3194dc39..e2e62363 100644 --- a/taiga/projects/milestones/api.py +++ b/taiga/projects/milestones/api.py @@ -14,16 +14,18 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +from django.apps import apps + from taiga.base import filters from taiga.base import response from taiga.base.decorators import detail_route from taiga.base.api import ModelCrudViewSet, ModelListViewSet from taiga.base.api.utils import get_object_or_404 +from taiga.base.utils.db import get_object_or_none from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.history.mixins import HistoryResourceMixin - from . import serializers from . import models from . import permissions @@ -38,6 +40,26 @@ class MilestoneViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCrudView filter_fields = ("project", "closed") queryset = models.Milestone.objects.all() + def list(self, request, *args, **kwargs): + res = super().list(request, *args, **kwargs) + self._add_taiga_info_headers() + return res + + def _add_taiga_info_headers(self): + try: + project_id = int(self.request.QUERY_PARAMS.get("project", None)) + project_model = apps.get_model("projects", "Project") + project = get_object_or_none(project_model, id=project_id) + except TypeError: + project = None + + if project: + opened_milestones = project.milestones.filter(closed=False).count() + closed_milestones = project.milestones.filter(closed=True).count() + + self.headers["Taiga-Info-Total-Opened-Milestones"] = opened_milestones + self.headers["Taiga-Info-Total-Closed-Milestones"] = closed_milestones + def get_queryset(self): qs = super().get_queryset() qs = self.attach_watchers_attrs_to_queryset(qs) diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index a3737488..9869159a 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -228,13 +228,6 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi field=order_field) services.snapshot_userstories_in_bulk(data["bulk_stories"], request.user) - if order_field in ["sprint_order", "backlog_order"]: - # NOTE: This is useful according to issue #2851 to update sprints column in - # the browser client when move USs from the backlog to an sprint, from - # an sprint to the backlog or between sprints. - has_closed_milestones = project.milestones.filter(closed=True).exists() - self.headers["Taiga-Info-Has-Closed-Milestones"] = has_closed_milestones - return response.NoContent() @list_route(methods=["POST"]) diff --git a/tests/integration/test_milestones.py b/tests/integration/test_milestones.py index 1b3410a1..932da487 100644 --- a/tests/integration/test_milestones.py +++ b/tests/integration/test_milestones.py @@ -47,3 +47,36 @@ def test_update_milestone_with_userstories_list(client): client.login(user) response = client.json.patch(url, json.dumps(form_data)) assert response.status_code == 200 + + +def test_list_milestones_taiga_info_headers(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + role = f.RoleFactory.create(project=project) + f.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) + + f.MilestoneFactory.create(project=project, owner=user, closed=True) + f.MilestoneFactory.create(project=project, owner=user, closed=True) + f.MilestoneFactory.create(project=project, owner=user, closed=True) + f.MilestoneFactory.create(project=project, owner=user, closed=False) + f.MilestoneFactory.create(owner=user, closed=False) + + url = reverse("milestones-list") + + client.login(project.owner) + response1 = client.json.get(url) + response2 = client.json.get(url, {"project": project.id}) + + assert response1.status_code == 200 + assert "taiga-info-total-closed-milestones" in response1["access-control-expose-headers"] + assert "taiga-info-total-opened-milestones" in response1["access-control-expose-headers"] + assert response1.has_header("Taiga-Info-Total-Closed-Milestones") == False + assert response1.has_header("Taiga-Info-Total-Opened-Milestones") == False + + assert response2.status_code == 200 + assert "taiga-info-total-closed-milestones" in response2["access-control-expose-headers"] + assert "taiga-info-total-opened-milestones" in response2["access-control-expose-headers"] + assert response2.has_header("Taiga-Info-Total-Closed-Milestones") == True + assert response2.has_header("Taiga-Info-Total-Opened-Milestones") == True + assert response2["taiga-info-total-closed-milestones"] == "3" + assert response2["taiga-info-total-opened-milestones"] == "1" diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 1eaea1b1..33b18a8d 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -134,51 +134,6 @@ def test_api_update_orders_in_bulk(client): assert response2.status_code == 204, response2.data assert response3.status_code == 204, response3.data -def test_api_update_orders_in_bulk_to_test_extra_headers(client): - project = f.create_project() - f.MembershipFactory.create(project=project, user=project.owner, is_owner=True) - us1 = f.create_userstory(project=project) - us2 = f.create_userstory(project=project) - - url1 = reverse("userstories-bulk-update-backlog-order") - url2 = reverse("userstories-bulk-update-kanban-order") - url3 = reverse("userstories-bulk-update-sprint-order") - - data = { - "project_id": project.id, - "bulk_stories": [{"us_id": us1.id, "order": 1}, - {"us_id": us2.id, "order": 2}] - } - - client.login(project.owner) - - response1 = client.json.post(url1, json.dumps(data)) - response2 = client.json.post(url2, json.dumps(data)) - response3 = client.json.post(url3, json.dumps(data)) - assert response1.status_code == 204 - assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == True - assert response1["taiga-info-has-closed-milestones"] == "False" - assert response2.status_code == 204 - assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False - assert response3.status_code == 204 - assert response3.has_header("Taiga-Info-Has-Closed-Milestones") == True - assert response3["taiga-info-has-closed-milestones"] == "False" - - us1.milestone.closed = True - us1.milestone.save() - - response1 = client.json.post(url1, json.dumps(data)) - response2 = client.json.post(url2, json.dumps(data)) - response3 = client.json.post(url3, json.dumps(data)) - assert response1.status_code == 204 - assert response1.has_header("Taiga-Info-Has-Closed-Milestones") == True - assert response3["taiga-info-has-closed-milestones"] == "True" - assert response2.status_code == 204 - assert response2.has_header("Taiga-Info-Has-Closed-Milestones") == False - assert response3.status_code == 204 - assert response3.has_header("Taiga-Info-Has-Closed-Milestones") == True - assert response3["taiga-info-has-closed-milestones"] == "True" - def test_update_userstory_points(client): user1 = f.UserFactory.create() From a9710fee7ca65ef85da4709fdfe99cecb8be58dd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 2 Sep 2015 11:20:07 +0200 Subject: [PATCH 112/190] Refactoring notify policies --- taiga/projects/api.py | 11 +++++++++ taiga/projects/notifications/api.py | 15 ++++++------ taiga/projects/notifications/services.py | 11 +++++++++ taiga/projects/serializers.py | 20 +++++++++++++++ tests/integration/test_watch_projects.py | 31 ++++++++++++++++++++++++ 5 files changed, 80 insertions(+), 8 deletions(-) diff --git a/taiga/projects/api.py b/taiga/projects/api.py index baaa0eb0..93b08a87 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -32,6 +32,7 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin +from taiga.projects.notifications.services import set_notify_policy from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -66,6 +67,16 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi qs = self.attach_votes_attrs_to_queryset(qs) return self.attach_watchers_attrs_to_queryset(qs) + @detail_route(methods=["POST"]) + def watch(self, request, pk=None): + response = super(ProjectViewSet, self).watch(request, pk) + notify_policy = self.get_object().notify_policies.get(user=request.user) + level = request.DATA.get("notify_level", None) + if level is not None: + set_notify_policy(notify_policy, level) + + return response + @list_route(methods=["POST"]) def bulk_update_order(self, request, **kwargs): if self.request.user.is_anonymous(): diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py index b2f2d260..453df821 100644 --- a/taiga/projects/notifications/api.py +++ b/taiga/projects/notifications/api.py @@ -33,13 +33,12 @@ class NotifyPolicyViewSet(ModelCrudViewSet): permission_classes = (permissions.NotifyPolicyPermission,) def _build_needed_notify_policies(self): - watched_content = user_services.get_watched_content_for_user(self.request.user) - watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct() + watched_project_ids = user_services.get_watched_content_for_user(self.request.user).get("project", []) projects = Project.objects.filter( Q(owner=self.request.user) | Q(memberships__user=self.request.user) | - Q(id__in=watched_content_project_ids) + Q(id__in=watched_project_ids) ).distinct() for project in projects: @@ -54,10 +53,10 @@ class NotifyPolicyViewSet(ModelCrudViewSet): # With really want to include the policies related to any content: # - The user is the owner of the project # - The user is member of the project - # - The user is watching any object from the project - watched_content = user_services.get_watched_content_for_user(self.request.user) - watched_content_project_ids = watched_content.values_list("project__id", flat=True).distinct() - return models.NotifyPolicy.objects.filter(Q(project__owner=self.request.user) | + # - The user is watching the project + watched_project_ids = user_services.get_watched_content_for_user(self.request.user).get("project", []) + + return models.NotifyPolicy.objects.filter(user=self.request.user).filter(Q(project__owner=self.request.user) | Q(project__memberships__user=self.request.user) | - Q(project__id__in=watched_content_project_ids) + Q(project__id__in=watched_project_ids) ).distinct() diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index adc94a69..778e2770 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -356,3 +356,14 @@ def remove_watcher(obj, user): return qs.delete() + + +def set_notify_policy(notify_policy, notify_level): + """ + Set notification level for specified policy. + """ + if not notify_level in [e.value for e in NotifyLevel]: + raise exc.IntegrityError(_("Invalid value for notify level")) + + notify_policy.notify_level = notify_level + notify_policy.save() diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index c4f5f506..44812e51 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -36,6 +36,9 @@ from taiga.users.validators import RoleExistsValidator from taiga.permissions.service import get_user_project_permissions from taiga.permissions.service import is_project_owner +from taiga.projects.notifications import models as notify_models +from taiga.projects.notifications import serializers as notify_serializers + from . import models from . import services from .validators import ProjectExistsValidator @@ -367,6 +370,7 @@ class ProjectDetailSerializer(ProjectSerializer): roles = ProjectRoleSerializer(source="roles", many=True, read_only=True) members = serializers.SerializerMethodField(method_name="get_members") + notify_policy = serializers.SerializerMethodField(method_name="get_notify_policy") def get_members(self, obj): qs = obj.memberships.filter(user__isnull=False) @@ -376,6 +380,22 @@ class ProjectDetailSerializer(ProjectSerializer): serializer = ProjectMemberSerializer(qs, many=True) return serializer.data + def get_notify_policy(self, obj): + request= self.context.get("request", None) + if request is None: + return None + + user = request.user + if not user.is_authenticated(): + return None + + try: + notify_policy = obj.notify_policies.get(user=user, project=obj) + return notify_serializers.NotifyPolicySerializer(notify_policy).data + + except notify_models.NotifyPolicy.DoesNotExist: + return None + class ProjectDetailAdminSerializer(ProjectDetailSerializer): class Meta: diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 358c15f2..5d643eb4 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -36,6 +36,37 @@ def test_watch_project(client): assert response.status_code == 200 +def test_watch_project_with_valid_notify_level(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-watch", args=(project.id,)) + + client.login(user) + data = { + "notify_level": 1 + } + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 200 + + +def test_watch_project_with_invalid_notify_level(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-watch", args=(project.id,)) + + client.login(user) + data = { + "notify_level": 333 + } + response = client.json.post(url, json.dumps(data)) + + assert response.status_code == 400 + assert response.data["_error_message"] == "Invalid value for notify level" + + def test_unwacth_project(client): user = f.UserFactory.create() project = f.create_project(owner=user) From fb710e298120f115d616ef2aed0a42b359331ab1 Mon Sep 17 00:00:00 2001 From: Brett Profitt Date: Tue, 11 Aug 2015 13:15:22 -0400 Subject: [PATCH 113/190] Added notification threading. Fixes #409. --- AUTHORS.rst | 1 + CHANGELOG.md | 1 + taiga/projects/notifications/services.py | 59 +++++++++++++- tests/integration/test_notifications.py | 98 ++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 95154a8a..e7b493df 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -25,3 +25,4 @@ answer newbie questions, and generally made taiga that much better: - Julien Palard - Ricky Posner - Yamila Moreno +- Brett Profitt diff --git a/CHANGELOG.md b/CHANGELOG.md index 05daf163..55f931f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - Project can be starred or unstarred and the fans list can be obtained. - Now users can watch public issues, tasks and user stories. - Add endpoints to show the watchers list for issues, tasks and user stories. +- Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). - i18n. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 778e2770..3951622a 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -14,6 +14,8 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import datetime + from functools import partial from django.apps import apps @@ -256,7 +258,7 @@ def send_sync_notifications(notification_id): """ notification = HistoryChangeNotification.objects.select_for_update().get(pk=notification_id) - # If the las modification is too recent we ignore it + # If the last modification is too recent we ignore it now = timezone.now() time_diff = now - notification.updated_datetime if time_diff.seconds < settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL: @@ -275,11 +277,34 @@ def send_sync_notifications(notification_id): model = get_model_from_key(notification.key) template_name = _resolve_template_name(model, change_type=notification.history_type) email = _make_template_mail(template_name) + domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] + + if "ref" in obj.snapshot: + msg_id = obj.snapshot["ref"] + elif "slug" in obj.snapshot: + msg_id = obj.snapshot["slug"] + else: + msg_id = 'taiga-system' + + now = datetime.datetime.now() + format_args = {"project_slug": notification.project.slug, + "project_name": notification.project.name, + "msg_id": msg_id, + "time": int(now.timestamp()), + "domain": domain} + + headers = {"Message-ID": "<{project_slug}/{msg_id}/{time}@{domain}>".format(**format_args), + "In-Reply-To": "<{project_slug}/{msg_id}@{domain}>".format(**format_args), + "References": "<{project_slug}/{msg_id}@{domain}>".format(**format_args), + + "List-ID": 'Taiga/{project_name} '.format(**format_args), + + "Thread-Index": make_ms_thread_index("<{project_slug}/{msg_id}@{domain}>".format(**format_args), now)} for user in notification.notify_users.distinct(): context["user"] = user context["lang"] = user.lang or settings.LANGUAGE_CODE - email.send(user.email, context) + email.send(user.email, context, headers=headers) notification.delete() @@ -367,3 +392,33 @@ def set_notify_policy(notify_policy, notify_level): notify_policy.notify_level = notify_level notify_policy.save() + + +def make_ms_thread_index(msg_id, dt): + """ + Create the 22-byte base of the thread-index string in the format: + + 6 bytes = First 6 significant bytes of the FILETIME stamp + 16 bytes = GUID (we're using a md5 hash of the message id) + + See http://www.meridiandiscovery.com/how-to/e-mail-conversation-index-metadata-computer-forensics/ + """ + + import base64 + import hashlib + import struct + + # Convert to FILETIME epoch (microseconds since 1601) + delta = datetime.date(1970, 1, 1) - datetime.date(1601, 1, 1) + filetime = int(dt.timestamp() + delta.total_seconds()) * 10000000 + + # only want the first 6 bytes + thread_bin = struct.pack(">Q", filetime)[:6] + + # Make a guid. This is usually generated by Outlook. + # The format is usually >IHHQ, but we don't care since it's just a hash of the id + md5 = hashlib.md5(msg_id.encode('utf-8')) + thread_bin += md5.digest() + + # base64 encode + return base64.b64encode(thread_bin) diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 87ea400d..1a1b98b0 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -17,6 +17,13 @@ import pytest import time +import math +import base64 +import datetime +import hashlib +import binascii +import struct + from unittest.mock import MagicMock, patch from django.core.urlresolvers import reverse @@ -409,6 +416,63 @@ def test_send_notifications_using_services_method(settings, mail): services.process_sync_notifications() assert len(mail.outbox) == 12 + # test headers + events = [issue, us, task, wiki] + domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] + i = 0 + for msg in mail.outbox: + # each event has 3 msgs + event = events[math.floor(i / 3)] + + # each set of 3 should have the same headers + if i % 3 == 0: + if hasattr(event, 'ref'): + e_slug = event.ref + elif hasattr(event, 'slug'): + e_slug = event.slug + else: + e_slug = 'taiga-system' + + m_id = "{project_slug}/{msg_id}".format( + project_slug=project.slug, + msg_id=e_slug + ) + + message_id = "<{m_id}/".format(m_id=m_id) + message_id_domain = "@{domain}>".format(domain=domain) + in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) + list_id = "Taiga/{p_name} " \ + .format(p_name=project.name, p_slug=project.slug, domain=domain) + + assert msg.extra_headers + headers = msg.extra_headers + + # can't test the time part because it's set when sending + # check what we can + assert 'Message-ID' in headers + assert message_id in headers.get('Message-ID') + assert message_id_domain in headers.get('Message-ID') + + assert 'In-Reply-To' in headers + assert in_reply_to == headers.get('In-Reply-To') + assert 'References' in headers + assert in_reply_to == headers.get('References') + + assert 'List-ID' in headers + assert list_id == headers.get('List-ID') + + assert 'Thread-Index' in headers + # always is b64 encoded 22 bytes + assert len(base64.b64decode(headers.get('Thread-Index'))) == 22 + + # hashes should match for identical ids and times + # we check the actual method in test_ms_thread_id() + msg_time = headers.get('Message-ID').split('/')[2].split('@')[0] + msg_ts = datetime.datetime.fromtimestamp(int(msg_time)) + assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index') + + i += 1 + def test_resource_notification_test(client, settings, mail): settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 @@ -613,3 +677,37 @@ def test_retrieve_notify_policies_by_anonymous_user(client): response = client.get(url, content_type="application/json") assert response.status_code == 404, response.status_code assert response.data["_error_message"] == "No NotifyPolicy matches the given query.", str(response.content) + + +def test_ms_thread_id(): + id = '' + now = datetime.datetime.now() + + index = services.make_ms_thread_index(id, now) + parsed = parse_ms_thread_index(index) + + assert parsed[0] == hashlib.md5(id.encode('utf-8')).hexdigest() + # always only one time + assert (now - parsed[1][0]).seconds <= 2 + + +# see http://stackoverflow.com/questions/27374077/parsing-thread-index-mail-header-with-python +def parse_ms_thread_index(index): + s = base64.b64decode(index) + + # ours are always md5 digests + guid = binascii.hexlify(s[6:22]).decode('utf-8') + + # if we had real guids, we'd do something like + # guid = struct.unpack('>IHHQ', s[6:22]) + # guid = '%08X-%04X-%04X-%04X-%12X' % (guid[0], guid[1], guid[2], (guid[3] >> 48) & 0xFFFF, guid[3] & 0xFFFFFFFFFFFF) + + f = struct.unpack('>Q', s[:6] + b'\0\0')[0] + ts = [datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=f//10)] + + # for the 5 byte appendixes that we won't use + for n in range(22, len(s), 5): + f = struct.unpack('>I', s[n:n+4])[0] + ts.append(ts[-1] + datetime.timedelta(microseconds=(f << 18)//10)) + + return guid, ts From 58807fc964e8fc4b10b7377e9458490704229bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 3 Sep 2015 09:43:13 +0200 Subject: [PATCH 114/190] Change notify_policy to return the notify level ('int') instead the unnecessary NotifyPolicy object --- taiga/projects/serializers.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 44812e51..f2930f55 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -37,7 +37,6 @@ from taiga.permissions.service import get_user_project_permissions from taiga.permissions.service import is_project_owner from taiga.projects.notifications import models as notify_models -from taiga.projects.notifications import serializers as notify_serializers from . import models from . import services @@ -391,7 +390,7 @@ class ProjectDetailSerializer(ProjectSerializer): try: notify_policy = obj.notify_policies.get(user=user, project=obj) - return notify_serializers.NotifyPolicySerializer(notify_policy).data + return notify_policy.notify_level except notify_models.NotifyPolicy.DoesNotExist: return None From 014941aec6c7ee63c10b301d27fbf690452ca66b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 3 Sep 2015 11:02:58 +0200 Subject: [PATCH 115/190] Adding notify_level to queryset for projects API --- taiga/projects/api.py | 8 ++++-- taiga/projects/notifications/mixins.py | 2 +- taiga/projects/serializers.py | 18 +----------- taiga/projects/utils.py | 40 ++++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 21 deletions(-) create mode 100644 taiga/projects/utils.py diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 93b08a87..dffa03fa 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -45,13 +45,13 @@ from . import serializers from . import models from . import permissions from . import services +from . import utils from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin ###################################################### ## Project ###################################################### - class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer @@ -65,7 +65,9 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi def get_queryset(self): qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) - return self.attach_watchers_attrs_to_queryset(qs) + qs = self.attach_watchers_attrs_to_queryset(qs) + qs = utils.attach_notify_level_to_queryset(qs, self.request.user) + return qs @detail_route(methods=["POST"]) def watch(self, request, pk=None): @@ -74,7 +76,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi level = request.DATA.get("notify_level", None) if level is not None: set_notify_policy(notify_policy, level) - + return response @list_route(methods=["POST"]) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 75740a3d..627883b5 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -214,7 +214,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it - if not hasattr(obj, "watchers"): + if obj is not None and not hasattr(obj, "watchers"): obj.watchers = [user.id for user in services.get_watchers(obj)] return super(WatchedResourceModelSerializer, self).to_native(obj) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index f2930f55..b986e6ff 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -318,6 +318,7 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched i_am_owner = serializers.SerializerMethodField("get_i_am_owner") tags_colors = TagsColorsField(required=False) total_closed_milestones = serializers.SerializerMethodField("get_total_closed_milestones") + notify_level = serializers.IntegerField(read_only=True) class Meta: model = models.Project @@ -369,7 +370,6 @@ class ProjectDetailSerializer(ProjectSerializer): roles = ProjectRoleSerializer(source="roles", many=True, read_only=True) members = serializers.SerializerMethodField(method_name="get_members") - notify_policy = serializers.SerializerMethodField(method_name="get_notify_policy") def get_members(self, obj): qs = obj.memberships.filter(user__isnull=False) @@ -379,22 +379,6 @@ class ProjectDetailSerializer(ProjectSerializer): serializer = ProjectMemberSerializer(qs, many=True) return serializer.data - def get_notify_policy(self, obj): - request= self.context.get("request", None) - if request is None: - return None - - user = request.user - if not user.is_authenticated(): - return None - - try: - notify_policy = obj.notify_policies.get(user=user, project=obj) - return notify_policy.notify_level - - except notify_models.NotifyPolicy.DoesNotExist: - return None - class ProjectDetailAdminSerializer(ProjectDetailSerializer): class Meta: diff --git a/taiga/projects/utils.py b/taiga/projects/utils.py new file mode 100644 index 00000000..356aa769 --- /dev/null +++ b/taiga/projects/utils.py @@ -0,0 +1,40 @@ +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino +# Copyright (C) 2014 David Barragán +# Copyright (C) 2014 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.apps import apps + +def attach_notify_level_to_queryset(queryset, user, as_field="notify_level"): + """Attach notify level to each object of the queryset. + + :param queryset: A Django queryset object. + :param as_field: Attach the notify level as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + + if user.is_authenticated(): + model = queryset.model + sql = ("""SELECT notifications_notifypolicy.notify_level + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.user_id = {user_id} + AND notifications_notifypolicy.project_id = {tbl}.id""") + sql = sql.format(user_id=user.id, tbl=model._meta.db_table) + else: + sql = "SELECT CAST(NULL as text)" + + qs = queryset.extra(select={as_field: sql}) + return qs From 285c759eb6bc27e3a3ea1852da0412d3d142b17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 4 Sep 2015 11:03:41 +0200 Subject: [PATCH 116/190] Fix tests errors --- taiga/projects/api.py | 5 ++- taiga/projects/notifications/services.py | 20 +++++++----- taiga/projects/serializers.py | 5 ++- taiga/projects/utils.py | 40 ------------------------ tests/integration/test_notifications.py | 6 ++-- 5 files changed, 22 insertions(+), 54 deletions(-) delete mode 100644 taiga/projects/utils.py diff --git a/taiga/projects/api.py b/taiga/projects/api.py index dffa03fa..2ea2a7cf 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -32,7 +32,7 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin -from taiga.projects.notifications.services import set_notify_policy +from taiga.projects.notifications.services import set_notify_policy, attach_notify_level_to_project_queryset from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -45,7 +45,6 @@ from . import serializers from . import models from . import permissions from . import services -from . import utils from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin @@ -66,7 +65,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) qs = self.attach_watchers_attrs_to_queryset(qs) - qs = utils.attach_notify_level_to_queryset(qs, self.request.user) + qs = attach_notify_level_to_project_queryset(qs, self.request.user) return qs @detail_route(methods=["POST"]) diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 3951622a..4a954fe6 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -90,22 +90,28 @@ def get_notify_policy(project, user): return instance -def attach_notify_policy_to_project_queryset(current_user, queryset): +def attach_notify_level_to_project_queryset(queryset, user): """ Function that attach "notify_level" attribute on each queryset result for query notification level of current user for each project in the most efficient way. + + :param queryset: A Django queryset object. + :param user: A User model object. + + :return: Queryset object with the additional `as_field` field. """ + user_id = getattr(user, "id", None) or "NULL" + default_level = NotifyLevel.notwatch sql = strip_lines(""" COALESCE((SELECT notifications_notifypolicy.notify_level - FROM notifications_notifypolicy - WHERE notifications_notifypolicy.project_id = projects_project.id - AND notifications_notifypolicy.user_id = {userid}), {default_level}) + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.project_id = projects_project.id + AND notifications_notifypolicy.user_id = {user_id}), + {default_level}) """) - - sql = sql.format(userid=current_user.pk, - default_level=NotifyLevel.notwatch) + sql = sql.format(user_id=user_id, default_level=default_level) return queryset.extra(select={"notify_level": sql}) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index b986e6ff..2f3b0ab1 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -318,7 +318,7 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched i_am_owner = serializers.SerializerMethodField("get_i_am_owner") tags_colors = TagsColorsField(required=False) total_closed_milestones = serializers.SerializerMethodField("get_total_closed_milestones") - notify_level = serializers.IntegerField(read_only=True) + notify_level = serializers.SerializerMethodField("get_notify_level") class Meta: model = models.Project @@ -339,6 +339,9 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched def get_total_closed_milestones(self, obj): return obj.milestones.filter(closed=True).count() + def get_notify_level(self, obj): + return getattr(obj, "notify_level", None) + def validate_total_milestones(self, attrs, source): """ Check that total_milestones is not null, it's an optional parameter but diff --git a/taiga/projects/utils.py b/taiga/projects/utils.py deleted file mode 100644 index 356aa769..00000000 --- a/taiga/projects/utils.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from django.apps import apps - -def attach_notify_level_to_queryset(queryset, user, as_field="notify_level"): - """Attach notify level to each object of the queryset. - - :param queryset: A Django queryset object. - :param as_field: Attach the notify level as an attribute with this name. - - :return: Queryset object with the additional `as_field` field. - """ - - if user.is_authenticated(): - model = queryset.model - sql = ("""SELECT notifications_notifypolicy.notify_level - FROM notifications_notifypolicy - WHERE notifications_notifypolicy.user_id = {user_id} - AND notifications_notifypolicy.project_id = {tbl}.id""") - sql = sql.format(user_id=user.id, tbl=model._meta.db_table) - else: - sql = "SELECT CAST(NULL as text)" - - qs = queryset.extra(select={as_field: sql}) - return qs diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 1a1b98b0..183a581a 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -51,12 +51,12 @@ def mail(): return mail -def test_attach_notify_policy_to_project_queryset(): +def test_attach_notify_level_to_project_queryset(): project1 = f.ProjectFactory.create() f.ProjectFactory.create() qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_policy_to_project_queryset(project1.owner, qs) + qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) assert len(qs) == 2 assert qs[0].notify_level == NotifyLevel.notwatch @@ -64,7 +64,7 @@ def test_attach_notify_policy_to_project_queryset(): services.create_notify_policy(project1, project1.owner, NotifyLevel.watch) qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_policy_to_project_queryset(project1.owner, qs) + qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) assert qs[0].notify_level == NotifyLevel.watch assert qs[1].notify_level == NotifyLevel.notwatch From cd7e8fca2b88a975f759ee3780a59fc613ddb581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 4 Sep 2015 11:03:56 +0200 Subject: [PATCH 117/190] Remove some prints --- .../0007_set_bloked_note_and_is_blocked_in_snapshots.py | 3 +-- taiga/projects/issues/migrations/0003_auto_20141210_1108.py | 1 - taiga/projects/migrations/0013_auto_20141210_1040.py | 1 - taiga/projects/tasks/migrations/0004_auto_20141210_1107.py | 1 - .../userstories/migrations/0008_auto_20141210_1107.py | 1 - tests/utils.py | 4 ++-- 6 files changed, 3 insertions(+), 8 deletions(-) diff --git a/taiga/projects/history/migrations/0007_set_bloked_note_and_is_blocked_in_snapshots.py b/taiga/projects/history/migrations/0007_set_bloked_note_and_is_blocked_in_snapshots.py index dafe32ed..a1789405 100644 --- a/taiga/projects/history/migrations/0007_set_bloked_note_and_is_blocked_in_snapshots.py +++ b/taiga/projects/history/migrations/0007_set_bloked_note_and_is_blocked_in_snapshots.py @@ -15,7 +15,6 @@ def set_current_values_of_blocked_note_and_is_blocked_to_the_last_snapshot(apps, model = get_model_from_key(history_entry.key) pk = get_pk_from_key(history_entry.key) try: - print("Fixing history_entry: ", history_entry.created_at) obj = model.objects.get(pk=pk) save = False if hasattr(obj, "is_blocked") and "is_blocked" not in history_entry.snapshot: @@ -30,7 +29,7 @@ def set_current_values_of_blocked_note_and_is_blocked_to_the_last_snapshot(apps, history_entry.save() except ObjectDoesNotExist as e: - print("Ignoring {}".format(history_entry.pk)) + pass class Migration(migrations.Migration): diff --git a/taiga/projects/issues/migrations/0003_auto_20141210_1108.py b/taiga/projects/issues/migrations/0003_auto_20141210_1108.py index b8ee567c..ebc22056 100644 --- a/taiga/projects/issues/migrations/0003_auto_20141210_1108.py +++ b/taiga/projects/issues/migrations/0003_auto_20141210_1108.py @@ -21,7 +21,6 @@ def _fix_tags_model(tags_model): def fix_tags(apps, schema_editor): - print("Fixing user issue tags") _fix_tags_model(Issue) diff --git a/taiga/projects/migrations/0013_auto_20141210_1040.py b/taiga/projects/migrations/0013_auto_20141210_1040.py index 93c093bc..68bbb4f5 100644 --- a/taiga/projects/migrations/0013_auto_20141210_1040.py +++ b/taiga/projects/migrations/0013_auto_20141210_1040.py @@ -21,7 +21,6 @@ def _fix_tags_model(tags_model): def fix_tags(apps, schema_editor): - print("Fixing project tags") _fix_tags_model(Project) diff --git a/taiga/projects/tasks/migrations/0004_auto_20141210_1107.py b/taiga/projects/tasks/migrations/0004_auto_20141210_1107.py index b4c093f3..33d7c053 100644 --- a/taiga/projects/tasks/migrations/0004_auto_20141210_1107.py +++ b/taiga/projects/tasks/migrations/0004_auto_20141210_1107.py @@ -21,7 +21,6 @@ def _fix_tags_model(tags_model): def fix_tags(apps, schema_editor): - print("Fixing user task tags") _fix_tags_model(Task) diff --git a/taiga/projects/userstories/migrations/0008_auto_20141210_1107.py b/taiga/projects/userstories/migrations/0008_auto_20141210_1107.py index 8c4b6c8d..36e032fd 100644 --- a/taiga/projects/userstories/migrations/0008_auto_20141210_1107.py +++ b/taiga/projects/userstories/migrations/0008_auto_20141210_1107.py @@ -21,7 +21,6 @@ def _fix_tags_model(tags_model): def fix_tags(apps, schema_editor): - print("Fixing user story tags") _fix_tags_model(UserStory) diff --git a/tests/utils.py b/tests/utils.py index 96320972..8245e2dc 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -49,8 +49,8 @@ def _helper_test_http_method_responses(client, method, url, data, users, after_e response = getattr(client, method)(url, data, content_type=content_type) else: response = getattr(client, method)(url) - if response.status_code >= 400: - print("Response content:", response.content) + #if response.status_code >= 400: + # print("Response content:", response.content) results.append(response) From 8141ae694b66d65a69ee7c021fcf849800aaa99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 7 Sep 2015 11:45:58 +0200 Subject: [PATCH 118/190] [i18n] Update locales --- taiga/locale/pl/LC_MESSAGES/django.po | 23 +-- taiga/locale/pt_BR/LC_MESSAGES/django.po | 9 +- taiga/locale/ru/LC_MESSAGES/django.po | 203 ++++++++++++----------- 3 files changed, 125 insertions(+), 110 deletions(-) diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index aee49354..2a2ebba4 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -3,14 +3,15 @@ # This file is distributed under the same license as the taiga-back package. # # Translators: +# Wiktor Żurawik , 2015 # Wojtek Jurkowlaniec , 2015 msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-09-04 20:54+0000\n" +"Last-Translator: Wiktor Żurawik \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -47,7 +48,7 @@ msgstr "Nazwa użytkownika jest już używana." #: taiga/auth/services.py:78 msgid "Email is already in use." -msgstr "Ten adres email jest już w użyciu" +msgstr "Ten adres email jest już w użyciu." #: taiga/auth/services.py:94 msgid "Token not matches any valid invitation." @@ -1170,19 +1171,19 @@ msgstr "Modyfikuj link do Wiki" #: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 msgid "Star project" -msgstr "" +msgstr "Dodaj projekt od ulubionych" #: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 msgid "Vote user story" -msgstr "" +msgstr "Zagłosuj na historyjkę użytkownika" #: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 msgid "Vote task" -msgstr "" +msgstr "Zagłosuj na zadanie" #: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 msgid "Vote issue" -msgstr "" +msgstr "Zagłosuj na zgłoszenie" #: taiga/permissions/permissions.py:59 msgid "Add milestone" @@ -1860,12 +1861,12 @@ msgstr "powiadom użytkowników" #: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" -msgstr "" +msgstr "użytkownik" #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" -msgstr "" +msgstr "Obserwowane" #: taiga/projects/notifications/services.py:64 #: taiga/projects/notifications/services.py:78 @@ -3170,7 +3171,7 @@ msgstr "Nie ma statusu zadania z takim ID" #: taiga/projects/votes/models.py:28 msgid "count" -msgstr "" +msgstr "ilość" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 @@ -3199,7 +3200,7 @@ msgstr "href" #: taiga/timeline/signals.py:86 msgid "Check the history API for the exact diff" -msgstr "" +msgstr "Dla pełengo diffa sprawdź API historii" #: taiga/users/admin.py:50 msgid "Personal info" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 09b28df3..3ab2a23c 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -5,6 +5,7 @@ # Translators: # Cléber Zavadniak , 2015 # Thiago , 2015 +# Daniel Dias , 2015 # Kemel Zaidan , 2015 # Marlon Carvalho , 2015 # Renato Prado , 2015 @@ -14,8 +15,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-09-02 14:50+0000\n" +"Last-Translator: Daniel Dias \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -1862,7 +1863,7 @@ msgstr "notificar usuário" #: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" -msgstr "" +msgstr "usuário" #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 @@ -3151,7 +3152,7 @@ msgstr "Não há status de tarega com aquele id" #: taiga/projects/votes/models.py:28 msgid "count" -msgstr "" +msgstr "contagem" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 16b49b86..95daac8e 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" -"Last-Translator: Taiga Dev Team \n" +"PO-Revision-Date: 2015-08-30 17:58+0000\n" +"Last-Translator: Dmitry Vinokurov \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" @@ -443,7 +443,7 @@ msgstr "" #: taiga/base/templates/emails/hero-body-html.jinja:6 msgid "You have been Taigatized" -msgstr "Тайга поимела вас" +msgstr "Вы в Тайге" #: taiga/base/templates/emails/hero-body-html.jinja:359 msgid "" @@ -455,8 +455,8 @@ msgid "" " " msgstr "" "\n" -"

Тайга поимела вас!

\n" -"

Добро пожаловать в Taiga " +"

Вы в Тайге!

\n" +"

Добро пожаловать в Тайгу " "- инструмент с открытым исходным кодом для управления проектами в стиле " "Agile

\n" " " @@ -547,7 +547,7 @@ msgstr "ошибка импорта вики-ссылок" #: taiga/export_import/dump_service.py:164 msgid "error importing issues" -msgstr "ошибка импорта проблем" +msgstr "ошибка импорта задач" #: taiga/export_import/dump_service.py:169 msgid "error importing user stories" @@ -563,7 +563,7 @@ msgstr "ошибка импорта тэгов" #: taiga/export_import/dump_service.py:183 msgid "error importing timelines" -msgstr "ошибка импорта графиков" +msgstr "ошибка импорта хронологии проекта" #: taiga/export_import/serializers.py:163 msgid "{}=\"{}\" not found in this project" @@ -941,7 +941,7 @@ msgstr "Статус изменён из-за вклада с BitBucket" #: taiga/hooks/bitbucket/event_hooks.py:115 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" -msgstr "Неверная информация о проблеме" +msgstr "Неверная информация о задаче" #: taiga/hooks/bitbucket/event_hooks.py:131 #, python-brace-format @@ -953,22 +953,22 @@ msgid "" "\n" "{description}" msgstr "" -"Проблема создана [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " +"Задача создана [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " "профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" -"Изначальная проблема на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"Изначальная задача на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " "\"Перейти к 'bb#{number} - {subject}'\"):\n" "\n" "{description}" #: taiga/hooks/bitbucket/event_hooks.py:142 msgid "Issue created from BitBucket." -msgstr "Проблема создана из BitBucket." +msgstr "Задача создана из BitBucket." #: taiga/hooks/bitbucket/event_hooks.py:166 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" -msgstr "Неправильная информация о комментарии к проблеме" +msgstr "Неправильная информация в комментарии к задаче" #: taiga/hooks/bitbucket/event_hooks.py:174 #, python-brace-format @@ -982,7 +982,7 @@ msgid "" msgstr "" "Комментарий от [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " "профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" -"Изначальная проблема на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"Изначальная задача на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " "\"Перейти к 'bb#{number} - {subject}'\")\n" "\n" "{message}" @@ -1024,16 +1024,16 @@ msgid "" "\n" "{description}" msgstr "" -"Проблема создана [@{github_user_name}]({github_user_url} \"Посмотреть " -"профиль @{github_user_name} на GitHub\") из GitHub.\n" -"Изначальная проблема на GitHub: [gh#{number} - {subject}]({github_url} " -"\"Перейти к 'gh#{number} - {subject}'\"):\n" +"Задача создана [@{github_user_name}]({github_user_url} \"Посмотреть профиль " +"@{github_user_name} на GitHub\") из GitHub.\n" +"Исходная задача на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " +"к 'gh#{number} - {subject}'\"):\n" "\n" "{description}" #: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." -msgstr "Проблема создана из GitHub." +msgstr "Задача создана из GitHub." #: taiga/hooks/github/event_hooks.py:200 #, python-brace-format @@ -1047,8 +1047,8 @@ msgid "" msgstr "" "Комментарий от [@{github_user_name}]({github_user_url} \"Посмотреть профиль " "@{github_user_name} на GitHub\") из GitHub.\n" -"Изначальная проблема на GitHub: [gh#{number} - {subject}]({github_url} " -"\"Перейти к 'gh#{number} - {subject}'\")\n" +"Исходная задача на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " +"к 'gh#{number} - {subject}'\")\n" "\n" "{message}" @@ -1083,8 +1083,8 @@ msgid "" msgstr "" "Комментарий от [@{gitlab_user_name}]({gitlab_user_url} \"Посмотреть профиль " "@{gitlab_user_name} на GitLab\") из GitLab.\n" -"Изначальная проблема на GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go " -"to 'gl#{number} - {subject}'\")\n" +"Исходная задача на GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" "\n" "{message}" @@ -1121,7 +1121,7 @@ msgstr "Просмотреть задачи" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 #: taiga/permissions/permissions.py:75 msgid "View issues" -msgstr "Посмотреть проблемы" +msgstr "Посмотреть задачи" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 #: taiga/permissions/permissions.py:81 @@ -1151,11 +1151,11 @@ msgstr "Добавить комментарии к задачам" #: taiga/permissions/permissions.py:42 msgid "Add issues" -msgstr "Добавить проблемы" +msgstr "Добавить задачи" #: taiga/permissions/permissions.py:43 msgid "Add comments to issues" -msgstr "Добавить комментарии к проблемам" +msgstr "Добавить комментарии к задачам" #: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 msgid "Add wiki page" @@ -1175,19 +1175,19 @@ msgstr "Изменить wiki-ссылку" #: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 msgid "Star project" -msgstr "" +msgstr "Отметить проект" #: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 msgid "Vote user story" -msgstr "" +msgstr "Проголосовать за пользовательскую историю" #: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 msgid "Vote task" -msgstr "" +msgstr "Проголосовать за задачу" #: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 msgid "Vote issue" -msgstr "" +msgstr "Проголосовать за задачу" #: taiga/permissions/permissions.py:59 msgid "Add milestone" @@ -1231,15 +1231,15 @@ msgstr "Удалить задачу" #: taiga/permissions/permissions.py:76 msgid "Add issue" -msgstr "Добавить проблему" +msgstr "Добавить задачу" #: taiga/permissions/permissions.py:77 msgid "Modify issue" -msgstr "Изменить проблему" +msgstr "Изменить задачу" #: taiga/permissions/permissions.py:78 msgid "Delete issue" -msgstr "Удалить проблему" +msgstr "Удалить задачу" #: taiga/permissions/permissions.py:84 msgid "Delete wiki page" @@ -1418,7 +1418,7 @@ msgstr "задача" #: taiga/projects/custom_attributes/models.py:127 msgid "issue" -msgstr "проблема" +msgstr "задача" #: taiga/projects/custom_attributes/serializers.py:57 msgid "Already exists one with the same name." @@ -1559,27 +1559,25 @@ msgstr "спринт" #: taiga/projects/issues/api.py:159 msgid "You don't have permissions to set this sprint to this issue." -msgstr "" -"У вас нет прав для того чтобы установить такой спринт для этой проблемы" +msgstr "У вас нет прав для того чтобы установить такой спринт для этой задачи" #: taiga/projects/issues/api.py:163 msgid "You don't have permissions to set this status to this issue." -msgstr "" -"У вас нет прав для того чтобы установить такой статус для этой проблемы" +msgstr "У вас нет прав для того чтобы установить такой статус для этой задачи" #: taiga/projects/issues/api.py:167 msgid "You don't have permissions to set this severity to this issue." msgstr "" -"У вас нет прав для того чтобы установить такую важность для этой проблемы" +"У вас нет прав для того чтобы установить такую важность для этой задачи" #: taiga/projects/issues/api.py:171 msgid "You don't have permissions to set this priority to this issue." msgstr "" -"У вас нет прав для того чтобы установить такой приоритет для этой проблемы" +"У вас нет прав для того чтобы установить такой приоритет для этой задачи" #: taiga/projects/issues/api.py:175 msgid "You don't have permissions to set this type to this issue." -msgstr "У вас нет прав для того чтобы установить такой тип для этой проблемы" +msgstr "У вас нет прав для того чтобы установить такой тип для этой задачи" #: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 #: taiga/projects/userstories/models.py:57 @@ -1716,11 +1714,11 @@ msgstr "важность по умолчанию" #: taiga/projects/models.py:113 msgid "default issue status" -msgstr "статус проблемы по умолчанию" +msgstr "статус задачи по умолчанию" #: taiga/projects/models.py:117 msgid "default issue type" -msgstr "тип проблемы по умолчанию" +msgstr "тип задачи по умолчанию" #: taiga/projects/models.py:138 msgid "members" @@ -1748,7 +1746,7 @@ msgstr "активная wiki-панель" #: taiga/projects/models.py:151 taiga/projects/models.py:578 msgid "active issues panel" -msgstr "активная панель проблем" +msgstr "активная панель задач" #: taiga/projects/models.py:154 taiga/projects/models.py:581 msgid "videoconference system" @@ -1824,11 +1822,11 @@ msgstr "статусы задач" #: taiga/projects/models.py:589 msgid "issue statuses" -msgstr "статусы проблем" +msgstr "статусы задач" #: taiga/projects/models.py:590 msgid "issue types" -msgstr "типы проблем" +msgstr "типы задач" #: taiga/projects/models.py:591 msgid "priorities" @@ -1872,12 +1870,12 @@ msgstr "уведомить пользователей" #: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" -msgstr "" +msgstr "пользователь" #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" -msgstr "" +msgstr "Просмотренные" #: taiga/projects/notifications/services.py:64 #: taiga/projects/notifications/services.py:78 @@ -1897,12 +1895,12 @@ msgid "" " " msgstr "" "\n" -"

Проблема обновлена

\n" -"

Привет %(user)s,
%(changer)s обновил(а) проблему в %(project)sЗадача обновлена\n" +"

Привет %(user)s,
%(changer)s обновил(а) задачу в %(project)s\n" -"

Проблема #%(ref)s %(subject)s

\n" -" Просмотреть проблему\n" +"

Задача #%(ref)s %(subject)s

\n" +" Просмотреть задачу\n" " " #: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 @@ -1914,9 +1912,9 @@ msgid "" "See issue #%(ref)s %(subject)s at %(url)s\n" msgstr "" "\n" -"Проблема обновлена\n" -"Привет %(user)s, %(changer)s обновил(а) проблему %(project)s\n" -"Просмотреть проблему #%(ref)s %(subject)s можно по ссылке %(url)s\n" +"Задача обновлена\n" +"Привет %(user)s, %(changer)s обновил(а) задачу %(project)s\n" +"Просмотреть задачу #%(ref)s %(subject)s можно по ссылке %(url)s\n" #: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 #, python-format @@ -1925,7 +1923,7 @@ msgid "" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Обновлена проблема #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Обновлена задача #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 #, python-format @@ -1941,12 +1939,12 @@ msgid "" " " msgstr "" "\n" -"

Добавлена новая проблема

\n" -"

Привет %(user)s,
%(changer)s добавил(а) новую проблему в " +"

Добавлена новая задача

\n" +"

Привет %(user)s,
%(changer)s добавил(а) новую задачу в " "%(project)s

\n" -"

Проблема #%(ref)s %(subject)s

\n" -" Просмотреть проблему\n" +"

Задача #%(ref)s %(subject)s

\n" +" Просмотреть задачу\n" "

Команда Тайги

\n" " " @@ -1962,9 +1960,9 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Добавлена новая проблема\n" -"Привет %(user)s, %(changer)s добавил(а) новую проблему в %(project)s\n" -"Просмотреть проблему #%(ref)s %(subject)s можно по ссылке %(url)s\n" +"Добавлена новая задача\n" +"Привет %(user)s, %(changer)s добавил(а) новую задачу в %(project)s\n" +"Просмотреть задачу #%(ref)s %(subject)s можно по ссылке %(url)s\n" "\n" "---\n" "Команда Тайги\n" @@ -1976,7 +1974,7 @@ msgid "" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Добавлена проблема #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Добавлена задача #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 #, python-format @@ -1990,10 +1988,10 @@ msgid "" " " msgstr "" "\n" -"

Проблема удалена

\n" -"

Привет %(user)s,
%(changer)s удалил(а) проблему из %(project)sЗадача удалена\n" +"

Привет %(user)s,
%(changer)s удалил(а) задачу из %(project)s\n" -"

Проблема #%(ref)s %(subject)s

\n" +"

Задача #%(ref)s %(subject)s

\n" "

Команда Тайги

\n" " " @@ -2009,9 +2007,9 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Проблема удалена\n" -"Привет %(user)s, %(changer)s удалил(а) проблему из %(project)s\n" -"Проблема #%(ref)s %(subject)s\n" +"Задача удалена\n" +"Привет %(user)s, %(changer)s удалил(а) задачу из %(project)s\n" +"Задача #%(ref)s %(subject)s\n" "\n" "---\n" "Команда Тайги\n" @@ -2023,7 +2021,7 @@ msgid "" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Удалена проблема #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Удалена задача #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 #, python-format @@ -2656,11 +2654,11 @@ msgstr "Статусы задачи" #: taiga/projects/serializers.py:407 msgid "Issue's statuses" -msgstr "Статусы проблемы" +msgstr "Статусы задачи" #: taiga/projects/serializers.py:408 msgid "Issue's types" -msgstr "Типы проблемы" +msgstr "Типы задачи" #: taiga/projects/serializers.py:409 msgid "Priorities" @@ -2877,6 +2875,12 @@ msgid "" "then allowed to grow and change as more is learned about the product and its " "customers" msgstr "" +"Agile бэклог продукта в Scrum - это приоритезированный список пожеланий, " +"содержащий короткие описания всего функционала который должен быть в " +"продукте. При использовании Scrum, нет нужды начинать проект с длинного, " +"заранее составленного списка абсолютно всех требований. Scrum бэклог " +"продукта может расти и изменяться в ходе того как становится всё больше " +"известно о продукте и его пользователях." #. Translators: Name of kanban project template. #: taiga/projects/translations.py:33 @@ -2891,6 +2895,11 @@ msgid "" "process, from definition of a task to its delivery to the customer, is " "displayed for participants to see and team members pull work from a queue." msgstr "" +"Kanban - это метод организации работы со знаниями, особое внимание уделяющий " +"моментальной передаче информации без лишней нагрузки членов команды. При " +"этом подходе весь процесс, от создания задачи до её отправки заказчику, " +"отображается для участников в удобном виде и члены команды могут брать себе " +"задачи из очереди." #. Translators: User story point value (value = undefined) #: taiga/projects/translations.py:43 @@ -3006,7 +3015,7 @@ msgstr "Требуются подробности" #. Translators: Issue status #: taiga/projects/translations.py:122 msgid "Postponed" -msgstr "" +msgstr "Отложено" #. Translators: Issue status #: taiga/projects/translations.py:124 @@ -3092,7 +3101,7 @@ msgstr "Владелец продукта" #. Translators: User role #: taiga/projects/translations.py:180 msgid "Stakeholder" -msgstr "" +msgstr "Заинтересованная сторона" #: taiga/projects/userstories/api.py:155 msgid "You don't have permissions to set this sprint to this user story." @@ -3110,6 +3119,8 @@ msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " "{subject}\")" msgstr "" +"Генерируется пользовательская история [US #{ref} - {subject}](:us:{ref} \"US " +"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" @@ -3138,7 +3149,7 @@ msgstr "является требованием команды" #: taiga/projects/userstories/models.py:102 msgid "generated from issue" -msgstr "" +msgstr "создано из задачи" #: taiga/projects/userstories/validators.py:28 msgid "There's no user story with that id" @@ -3158,7 +3169,7 @@ msgstr "Не существует статуса задачи с таким ид #: taiga/projects/votes/models.py:28 msgid "count" -msgstr "" +msgstr "количество" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 @@ -3179,7 +3190,7 @@ msgstr "параметр 'project_id' является обязательным" #: taiga/projects/wiki/models.py:36 msgid "last modifier" -msgstr "" +msgstr "последний отредактировавший" #: taiga/projects/wiki/models.py:69 msgid "href" @@ -3199,7 +3210,7 @@ msgstr "Права доступа" #: taiga/users/admin.py:53 msgid "Important dates" -msgstr "" +msgstr "Важные даты" #: taiga/users/api.py:151 taiga/users/api.py:158 msgid "Invalid username or email" @@ -3215,11 +3226,11 @@ msgstr "" #: taiga/users/api.py:205 msgid "Current password parameter needed" -msgstr "" +msgstr "Поле \"текущий пароль\" является обязательным" #: taiga/users/api.py:208 msgid "New password parameter needed" -msgstr "" +msgstr "Поле \"новый пароль\" является обязательным" #: taiga/users/api.py:211 msgid "Invalid password length at least 6 charaters needed" @@ -3231,7 +3242,7 @@ msgstr "Неверно указан текущий пароль" #: taiga/users/api.py:230 msgid "Incomplete arguments" -msgstr "" +msgstr "Список аргументов неполон" #: taiga/users/api.py:235 msgid "Invalid image format" @@ -3243,16 +3254,16 @@ msgstr "Этот email уже используется" #: taiga/users/api.py:281 msgid "Not valid email" -msgstr "" +msgstr "Невалидный email" #: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" -msgstr "" +msgstr "Неверно, вы уверены что токен правильный и не использовался ранее?" #: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" -msgstr "" +msgstr "Неверно, вы уверены что токен правильный?" #: taiga/users/models.py:69 msgid "superuser status" @@ -3279,7 +3290,7 @@ msgstr "Введите корректное имя пользователя." #: taiga/users/models.py:107 msgid "active" -msgstr "" +msgstr "активный" #: taiga/users/models.py:108 msgid "" @@ -3297,7 +3308,7 @@ msgstr "фотография" #: taiga/users/models.py:118 msgid "date joined" -msgstr "" +msgstr "когда присоединился" #: taiga/users/models.py:120 msgid "default language" @@ -3313,11 +3324,11 @@ msgstr "временная зона по умолчанию" #: taiga/users/models.py:126 msgid "colorize tags" -msgstr "" +msgstr "установить цвета для тэгов" #: taiga/users/models.py:131 msgid "email token" -msgstr "" +msgstr "email токен" #: taiga/users/models.py:133 msgid "new email address" @@ -3325,11 +3336,11 @@ msgstr "новый email адрес" #: taiga/users/models.py:188 msgid "permissions" -msgstr "" +msgstr "разрешения" #: taiga/users/serializers.py:62 msgid "invalid" -msgstr "" +msgstr "невалидный" #: taiga/users/serializers.py:73 msgid "Invalid username. Try with a different one." @@ -3451,10 +3462,12 @@ msgid "" "\n" "You may remove your account from this service: %(url)s\n" msgstr "" +"\n" +"Вы можете удалить свой аккаунт из этого сервиса: %(url)s\n" #: taiga/users/templates/emails/registered_user-subject.jinja:1 msgid "You've been Taigatized!" -msgstr "" +msgstr "Вы в Тайге!" #: taiga/users/validators.py:29 msgid "There's no role with that id" @@ -3467,7 +3480,7 @@ msgstr "" #: taiga/userstorage/models.py:30 msgid "key" -msgstr "" +msgstr "ключ" #: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 msgid "URL" @@ -3479,7 +3492,7 @@ msgstr "Секретный ключ" #: taiga/webhooks/models.py:39 msgid "status code" -msgstr "" +msgstr "код статуса" #: taiga/webhooks/models.py:40 msgid "request data" From 4f761fe33fd9e5e49144ca2963b1856c5a9aea35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 7 Sep 2015 17:11:42 +0200 Subject: [PATCH 119/190] [i18n] Update locale catalog --- taiga/locale/en/LC_MESSAGES/django.po | 154 ++++++++++++-------------- 1 file changed, 71 insertions(+), 83 deletions(-) diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 4d0c20ce..8ba27299 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" +"POT-Creation-Date: 2015-09-07 17:00+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -525,9 +525,9 @@ msgid "It contain invalid custom fields." msgstr "" #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "" @@ -899,12 +899,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "" @@ -913,22 +913,22 @@ msgid "View user stories" msgstr "" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "" @@ -956,135 +956,119 @@ msgstr "" msgid "Add comments to issues" msgstr "" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:188 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:191 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:467 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:497 msgid "You don't have permisions to see that." msgstr "" @@ -1671,11 +1655,15 @@ msgstr "" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "" +#: taiga/projects/notifications/services.py:397 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2171,51 +2159,51 @@ msgstr "" msgid "You can't leave the project if there are no more owners" msgstr "" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "" @@ -2603,7 +2591,7 @@ msgstr "" msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:253 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2700,56 +2688,56 @@ msgstr "" msgid "Important dates" msgstr "" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "" -#: taiga/users/api.py:279 +#: taiga/users/api.py:278 msgid "Duplicated email" msgstr "" -#: taiga/users/api.py:281 +#: taiga/users/api.py:280 msgid "Not valid email" msgstr "" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:300 taiga/users/api.py:306 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:333 taiga/users/api.py:341 taiga/users/api.py:344 msgid "Invalid, are you sure the token is correct?" msgstr "" From 0cb8cfc52cd171ef5673df11c871f1a2c2abb1ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 8 Sep 2015 09:30:52 +0200 Subject: [PATCH 120/190] Set verbosity to 0 when update_all_contenttypes() --- taiga/projects/issues/migrations/0006_remove_issue_watchers.py | 2 +- .../milestones/migrations/0002_remove_milestone_watchers.py | 2 +- taiga/projects/tasks/migrations/0008_remove_task_watchers.py | 2 +- .../userstories/migrations/0010_remove_userstory_watchers.py | 2 +- taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py index dd3ee037..915ce22e 100644 --- a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py +++ b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() + update_all_contenttypes(verbosity=0) sql=""" INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) SELECT issue_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id diff --git a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py index 69d6aacd..162b6c1c 100644 --- a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py +++ b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() + update_all_contenttypes(verbosity=0) sql=""" INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) SELECT milestone_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id diff --git a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py index 4c934957..5a8b0618 100644 --- a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py +++ b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() + update_all_contenttypes(verbosity=0) sql=""" INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) SELECT task_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id diff --git a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py index 0d897aca..e2ff7538 100644 --- a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py +++ b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() + update_all_contenttypes(verbosity=0) sql=""" INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) SELECT userstory_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id diff --git a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py index f2cb8159..7d7af0b6 100644 --- a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py +++ b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py @@ -7,7 +7,7 @@ from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.management import update_all_contenttypes def create_notifications(apps, schema_editor): - update_all_contenttypes() + update_all_contenttypes(verbosity=0) sql=""" INSERT INTO notifications_watched (object_id, created_date, content_type_id, user_id, project_id) SELECT wikipage_id AS object_id, now() AS created_date, {content_type_id} AS content_type_id, user_id, project_id From fd46f00ccda16ed5c02d5acef9c535e259c241c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 8 Sep 2015 09:31:24 +0200 Subject: [PATCH 121/190] Update djmail to the nerw release 0.11 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 0098b044..d9016d71 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ pillow==2.5.3 pytz==2014.4 six==1.8.0 amqp==1.4.6 -djmail==0.10 +djmail==0.11 django-pgjson==0.2.2 djorm-pgarray==1.0.4 django-jinja==1.0.4 From 8c990e50882993490d7779c49b2f2209dcc78701 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 7 Sep 2015 15:20:57 +0200 Subject: [PATCH 122/190] Refactoring watchers for projects --- taiga/export_import/serializers.py | 6 +- taiga/projects/api.py | 35 +++++--- taiga/projects/models.py | 50 +++++++++++- taiga/projects/notifications/api.py | 16 +--- taiga/projects/notifications/mixins.py | 32 ++++---- taiga/projects/notifications/services.py | 79 +++++++++++-------- taiga/projects/notifications/utils.py | 76 +++++++++++++++++- taiga/timeline/signals.py | 29 ++----- taiga/users/services.py | 54 +++++++++++-- .../test_projects_resource.py | 13 +-- tests/integration/test_importer_api.py | 2 +- tests/integration/test_notifications.py | 47 +++++++---- tests/integration/test_timeline.py | 13 +-- tests/integration/test_watch_projects.py | 9 ++- 14 files changed, 316 insertions(+), 145 deletions(-) diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index 98e9d2cf..01f042cf 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -245,7 +245,7 @@ class WatcheableObjectModelSerializer(serializers.ModelSerializer): def save_watchers(self): new_watcher_emails = set(self._watchers) - old_watcher_emails = set(notifications_services.get_watchers(self.object).values_list("email", flat=True)) + old_watcher_emails = set(self.object.get_watchers().values_list("email", flat=True)) adding_watcher_emails = list(new_watcher_emails.difference(old_watcher_emails)) removing_watcher_emails = list(old_watcher_emails.difference(new_watcher_emails)) @@ -259,11 +259,11 @@ class WatcheableObjectModelSerializer(serializers.ModelSerializer): for user in removing_users: notifications_services.remove_watcher(self.object, user) - self.object.watchers = notifications_services.get_watchers(self.object) + self.object.watchers = self.object.get_watchers() def to_native(self, obj): ret = super(WatcheableObjectModelSerializer, self).to_native(obj) - ret["watchers"] = [user.email for user in notifications_services.get_watchers(obj)] + ret["watchers"] = [user.email for user in obj.get_watchers()] return ret diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 2ea2a7cf..8d3cfe1f 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -32,7 +32,12 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin -from taiga.projects.notifications.services import set_notify_policy, attach_notify_level_to_project_queryset +from taiga.projects.notifications.choices import NotifyLevel +from taiga.projects.notifications.utils import ( + attach_project_watchers_attrs_to_queryset, + attach_project_is_watched_to_queryset, + attach_notify_level_to_project_queryset) + from taiga.projects.mixins.ordering import BulkUpdateOrderMixin from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin @@ -48,10 +53,11 @@ from . import services from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin + ###################################################### ## Project ###################################################### -class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMixin, ModelCrudViewSet): +class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, ModelCrudViewSet): queryset = models.Project.objects.all() serializer_class = serializers.ProjectDetailSerializer admin_serializer_class = serializers.ProjectDetailAdminSerializer @@ -64,19 +70,28 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, WatchedResourceMi def get_queryset(self): qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) - qs = self.attach_watchers_attrs_to_queryset(qs) - qs = attach_notify_level_to_project_queryset(qs, self.request.user) + qs = attach_project_watchers_attrs_to_queryset(qs) + if self.request.user.is_authenticated(): + qs = attach_project_is_watched_to_queryset(qs, self.request.user) + qs = attach_notify_level_to_project_queryset(qs, self.request.user) + return qs @detail_route(methods=["POST"]) def watch(self, request, pk=None): - response = super(ProjectViewSet, self).watch(request, pk) - notify_policy = self.get_object().notify_policies.get(user=request.user) - level = request.DATA.get("notify_level", None) - if level is not None: - set_notify_policy(notify_policy, level) + project = self.get_object() + self.check_permissions(request, "watch", project) + notify_level = request.DATA.get("notify_level", NotifyLevel.watch) + project.add_watcher(self.request.user, notify_level=notify_level) + return response.Ok() - return response + @detail_route(methods=["POST"]) + def unwatch(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, "unwatch", project) + user = self.request.user + project.remove_watcher(user) + return response.Ok() @list_route(methods=["POST"]) def bulk_update_order(self, request, **kwargs): diff --git a/taiga/projects/models.py b/taiga/projects/models.py index b2e2566f..482d25da 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -20,7 +20,7 @@ import uuid from django.core.exceptions import ValidationError from django.db import models -from django.db.models import signals +from django.db.models import signals, Q from django.apps import apps from django.conf import settings from django.dispatch import receiver @@ -38,9 +38,14 @@ from taiga.base.utils.dicts import dict_sum from taiga.base.utils.sequence import arithmetic_progression from taiga.base.utils.slug import slugify_uniquely_for_queryset -from . import choices +from taiga.projects.notifications.choices import NotifyLevel +from taiga.projects.notifications.services import ( + get_notify_policy, + set_notify_policy_level, + set_notify_policy_level_to_ignore, + create_notify_policy_if_not_exists) -from . notifications.mixins import WatchedModelMixin +from . import choices class Membership(models.Model): @@ -73,6 +78,10 @@ class Membership(models.Model): user_order = models.IntegerField(default=10000, null=False, blank=False, verbose_name=_("user order")) + def get_related_people(self): + related_people = get_user_model().objects.filter(id=self.user.id) + return related_people + def clean(self): # TODO: Review and do it more robust memberships = Membership.objects.filter(user=self.user, project=self.project) @@ -120,7 +129,7 @@ class ProjectDefaults(models.Model): abstract = True -class Project(ProjectDefaults, WatchedModelMixin, TaggedMixin, models.Model): +class Project(ProjectDefaults, TaggedMixin, models.Model): name = models.CharField(max_length=250, null=False, blank=False, verbose_name=_("name")) slug = models.SlugField(max_length=250, unique=True, null=False, blank=True, @@ -334,6 +343,39 @@ class Project(ProjectDefaults, WatchedModelMixin, TaggedMixin, models.Model): "assigned": self._get_user_stories_points(assigned_user_stories), } + def _get_q_watchers(self): + return Q(notify_policies__project_id=self.id) & ~Q(notify_policies__notify_level=NotifyLevel.ignore) + + def get_watchers(self): + return get_user_model().objects.filter(self._get_q_watchers()) + + def get_related_people(self): + related_people_q = Q() + + ## - Owner + if self.owner_id: + related_people_q.add(Q(id=self.owner_id), Q.OR) + + ## - Watchers + related_people_q.add(self._get_q_watchers(), Q.OR) + + ## - Apply filters + related_people = get_user_model().objects.filter(related_people_q) + + ## - Exclude inactive and system users and remove duplicate + related_people = related_people.exclude(is_active=False) + related_people = related_people.exclude(is_system=True) + related_people = related_people.distinct() + return related_people + + def add_watcher(self, user, notify_level=NotifyLevel.watch): + notify_policy = create_notify_policy_if_not_exists(self, user) + set_notify_policy_level(notify_policy, notify_level) + + def remove_watcher(self, user): + notify_policy = get_notify_policy(self, user) + set_notify_policy_level_to_ignore(notify_policy) + class ProjectModulesConfig(models.Model): project = models.OneToOneField("Project", null=False, blank=False, diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py index 453df821..d4b92c96 100644 --- a/taiga/projects/notifications/api.py +++ b/taiga/projects/notifications/api.py @@ -33,12 +33,9 @@ class NotifyPolicyViewSet(ModelCrudViewSet): permission_classes = (permissions.NotifyPolicyPermission,) def _build_needed_notify_policies(self): - watched_project_ids = user_services.get_watched_content_for_user(self.request.user).get("project", []) - projects = Project.objects.filter( Q(owner=self.request.user) | - Q(memberships__user=self.request.user) | - Q(id__in=watched_project_ids) + Q(memberships__user=self.request.user) ).distinct() for project in projects: @@ -50,13 +47,6 @@ class NotifyPolicyViewSet(ModelCrudViewSet): self._build_needed_notify_policies() - # With really want to include the policies related to any content: - # - The user is the owner of the project - # - The user is member of the project - # - The user is watching the project - watched_project_ids = user_services.get_watched_content_for_user(self.request.user).get("project", []) - - return models.NotifyPolicy.objects.filter(user=self.request.user).filter(Q(project__owner=self.request.user) | - Q(project__memberships__user=self.request.user) | - Q(project__id__in=watched_project_ids) + return models.NotifyPolicy.objects.filter(user=self.request.user).filter( + Q(project__owner=self.request.user) | Q(project__memberships__user=self.request.user) ).distinct() diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 627883b5..e6e6fc74 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -51,7 +51,7 @@ class WatchedResourceMixin: def attach_watchers_attrs_to_queryset(self, queryset): qs = attach_watchers_to_queryset(queryset) if self.request.user.is_authenticated(): - qs = attach_is_watched_to_queryset(self.request.user, qs) + qs = attach_is_watched_to_queryset(qs, self.request.user) return qs @@ -126,21 +126,19 @@ class WatchedModelMixin(object): """ return self.project - def get_watchers(self) -> frozenset: + def get_watchers(self) -> object: """ Default implementation method for obtain a list of watchers for current instance. - - NOTE: the default implementation returns frozen - set of all watchers if "watchers" attribute exists - in a model. - - WARNING: it returns a full evaluated set and in - future, for project with 1000k watchers it can be - very inefficient way for obtain watchers but at - this momment is the simplest way. """ - return frozenset(services.get_watchers(self)) + return services.get_watchers(self) + + def get_related_people(self) -> object: + """ + Default implementation for obtain the related people of + current instance. + """ + return services.get_related_people(self) def get_watched(self, user_or_id): return services.get_watched(user_or_id, type(self)) @@ -194,7 +192,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): instance = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) if instance is not None and self.validate_watchers(attrs, "watchers"): new_watcher_ids = set(attrs.get("watchers", [])) - old_watcher_ids = set(services.get_watchers(instance).values_list("id", flat=True)) + old_watcher_ids = set(instance.get_watchers().values_list("id", flat=True)) adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) @@ -207,7 +205,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): for user in removing_users: services.remove_watcher(instance, user) - instance.watchers = services.get_watchers(instance) + instance.watchers = instance.get_watchers() return instance @@ -215,7 +213,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it if obj is not None and not hasattr(obj, "watchers"): - obj.watchers = [user.id for user in services.get_watchers(obj)] + obj.watchers = [user.id for user in obj.get_watchers()] return super(WatchedResourceModelSerializer, self).to_native(obj) @@ -235,7 +233,7 @@ class WatchersViewSetMixin: self.check_permissions(request, 'retrieve', resource) try: - self.object = services.get_watchers(resource).get(pk=pk) + self.object = resource.get_watchers().get(pk=pk) except ObjectDoesNotExist: # or User.DoesNotExist return response.NotFound() @@ -252,4 +250,4 @@ class WatchersViewSetMixin: def get_queryset(self): resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) - return services.get_watchers(resource) + return resource.get_watchers() diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 4a954fe6..5d191d32 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -21,6 +21,7 @@ from functools import partial from django.apps import apps from django.db.transaction import atomic from django.db import IntegrityError, transaction +from django.db.models import Q from django.contrib.contenttypes.models import ContentType from django.contrib.auth import get_user_model from django.utils import timezone @@ -30,7 +31,6 @@ from django.utils.translation import ugettext as _ from djmail import template_mail from taiga.base import exceptions as exc -from taiga.base.utils.text import strip_lines from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.history.choices import HistoryType from taiga.projects.history.services import (make_key_from_model_object, @@ -90,31 +90,6 @@ def get_notify_policy(project, user): return instance -def attach_notify_level_to_project_queryset(queryset, user): - """ - Function that attach "notify_level" attribute on each queryset - result for query notification level of current user for each - project in the most efficient way. - - :param queryset: A Django queryset object. - :param user: A User model object. - - :return: Queryset object with the additional `as_field` field. - """ - user_id = getattr(user, "id", None) or "NULL" - default_level = NotifyLevel.notwatch - - sql = strip_lines(""" - COALESCE((SELECT notifications_notifypolicy.notify_level - FROM notifications_notifypolicy - WHERE notifications_notifypolicy.project_id = projects_project.id - AND notifications_notifypolicy.user_id = {user_id}), - {default_level}) - """) - sql = sql.format(user_id=user_id, default_level=default_level) - return queryset.extra(select={"notify_level": sql}) - - def analize_object_for_watchers(obj:object, history:object): """ Generic implementation for analize model objects and @@ -182,9 +157,6 @@ def get_users_to_notify(obj, *, discard_users=None) -> list: candidates.update(filter(_can_notify_light, obj.project.get_watchers())) candidates.update(filter(_can_notify_light, obj.get_participants())) - #TODO: coger los watchers del proyecto que quieren ser notificados por correo - #Filtrar los watchers según su nivel de watched y su nivel en el proyecto - # Remove the changer from candidates if discard_users: candidates = candidates - set(discard_users) @@ -320,15 +292,49 @@ def process_sync_notifications(): send_sync_notifications(notification.pk) +def _get_q_watchers(obj): + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + return Q(watched__content_type=obj_type, watched__object_id=obj.id) + + def get_watchers(obj): """Get the watchers of an object. :param obj: Any Django model instance. - :return: User queryset object representing the users that voted the object. + :return: User queryset object representing the users that watch the object. """ - obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) - return get_user_model().objects.filter(watched__content_type=obj_type, watched__object_id=obj.id) + return get_user_model().objects.filter(_get_q_watchers(obj)) + + +def get_related_people(obj): + """Get the related people of an object for notifications. + + :param obj: Any Django model instance. + + :return: User queryset object representing the users related to the object. + """ + related_people_q = Q() + + ## - Owner + if hasattr(obj, "owner_id") and obj.owner_id: + related_people_q.add(Q(id=obj.owner_id), Q.OR) + + ## - Assigned to + if hasattr(obj, "assigned_to_id") and obj.assigned_to_id: + related_people_q.add(Q(id=obj.assigned_to_id), Q.OR) + + ## - Watchers + related_people_q.add(_get_q_watchers(obj), Q.OR) + + ## - Apply filters + related_people = get_user_model().objects.filter(related_people_q) + + ## - Exclude inactive and system users and remove duplicate + related_people = related_people.exclude(is_active=False) + related_people = related_people.exclude(is_system=True) + related_people = related_people.distinct() + return related_people def get_watched(user_or_id, model): @@ -389,7 +395,7 @@ def remove_watcher(obj, user): qs.delete() -def set_notify_policy(notify_policy, notify_level): +def set_notify_policy_level(notify_policy, notify_level): """ Set notification level for specified policy. """ @@ -400,6 +406,13 @@ def set_notify_policy(notify_policy, notify_level): notify_policy.save() +def set_notify_policy_level_to_ignore(notify_policy): + """ + Set notification level for specified policy. + """ + set_notify_policy_level(notify_policy, NotifyLevel.ignore) + + def make_ms_thread_index(msg_id, dt): """ Create the 22-byte base of the thread-index string in the format: diff --git a/taiga/projects/notifications/utils.py b/taiga/projects/notifications/utils.py index 14edd0b3..39e6eaff 100644 --- a/taiga/projects/notifications/utils.py +++ b/taiga/projects/notifications/utils.py @@ -16,7 +16,8 @@ # along with this program. If not, see . from django.apps import apps - +from .choices import NotifyLevel +from taiga.base.utils.text import strip_lines def attach_watchers_to_queryset(queryset, as_field="watchers"): """Attach watching user ids to each object of the queryset. @@ -39,7 +40,7 @@ def attach_watchers_to_queryset(queryset, as_field="watchers"): return qs -def attach_is_watched_to_queryset(user, queryset, as_field="is_watched"): +def attach_is_watched_to_queryset(queryset, user, as_field="is_watched"): """Attach is_watched boolean to each object of the queryset. :param user: A users.User object model @@ -61,3 +62,74 @@ def attach_is_watched_to_queryset(user, queryset, as_field="is_watched"): sql = sql.format(type_id=type.id, tbl=model._meta.db_table, user_id=user.id) qs = queryset.extra(select={as_field: sql}) return qs + + +def attach_project_is_watched_to_queryset(queryset, user, as_field="is_watched"): + """Attach is_watched boolean to each object of the projects queryset. + + :param user: A users.User object model + :param queryset: A Django projects queryset object. + :param as_field: Attach the boolean as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = ("""SELECT CASE WHEN (SELECT count(*) + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.project_id = {tbl}.id + AND notifications_notifypolicy.user_id = {user_id} + AND notifications_notifypolicy.notify_level != {ignore_notify_level}) > 0 + + THEN TRUE + ELSE FALSE + END""") + sql = sql.format(tbl=model._meta.db_table, user_id=user.id, ignore_notify_level=NotifyLevel.ignore) + qs = queryset.extra(select={as_field: sql}) + return qs + + +def attach_project_watchers_attrs_to_queryset(queryset, as_field="watchers"): + """Attach watching user ids to each project of the queryset. + + :param queryset: A Django projects queryset object. + :param as_field: Attach the watchers as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + + sql = ("""SELECT array(SELECT user_id + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.project_id = {tbl}.id + AND notifications_notifypolicy.notify_level != {ignore_notify_level})""") + sql = sql.format(tbl=model._meta.db_table, ignore_notify_level=NotifyLevel.ignore) + qs = queryset.extra(select={as_field: sql}) + + return qs + + +def attach_notify_level_to_project_queryset(queryset, user): + """ + Function that attach "notify_level" attribute on each queryset + result for query notification level of current user for each + project in the most efficient way. + + :param queryset: A Django queryset object. + :param user: A User model object. + + :return: Queryset object with the additional `as_field` field. + """ + user_id = getattr(user, "id", None) or "NULL" + default_level = NotifyLevel.notwatch + + sql = strip_lines(""" + COALESCE((SELECT notifications_notifypolicy.notify_level + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.project_id = projects_project.id + AND notifications_notifypolicy.user_id = {user_id}), + {default_level}) + """) + sql = sql.format(user_id=user_id, default_level=default_level) + return queryset.extra(select={"notify_level": sql}) diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 5673fbbb..1a379f13 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -45,31 +45,12 @@ def _push_to_timelines(project, user, obj, event_type, created_datetime, extra_d namespace=build_project_namespace(project), extra_data=extra_data) - ## User profile timelines - ## - Me - related_people = User.objects.filter(id=user.id) + if hasattr(obj, "get_related_people"): + related_people = obj.get_related_people() - ## - Owner - if hasattr(obj, "owner_id") and obj.owner_id: - related_people |= User.objects.filter(id=obj.owner_id) - - ## - Assigned to - if hasattr(obj, "assigned_to_id") and obj.assigned_to_id: - related_people |= User.objects.filter(id=obj.assigned_to_id) - - ## - Watchers - watchers = notifications_services.get_watchers(obj) - if watchers: - related_people |= watchers - - ## - Exclude inactive and system users and remove duplicate - related_people = related_people.exclude(is_active=False) - related_people = related_people.exclude(is_system=True) - related_people = related_people.distinct() - - _push_to_timeline(related_people, obj, event_type, created_datetime, - namespace=build_user_namespace(user), - extra_data=extra_data) + _push_to_timeline(related_people, obj, event_type, created_datetime, + namespace=build_user_namespace(user), + extra_data=extra_data) else: # Actions not related with a project ## - Me diff --git a/taiga/users/services.py b/taiga/users/services.py index 992f4ae8..0ce318bd 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -22,6 +22,7 @@ from django.apps import apps from django.db.models import Q from django.db import connection from django.conf import settings +from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ from easy_thumbnails.files import get_thumbnailer @@ -29,6 +30,7 @@ from easy_thumbnails.exceptions import InvalidImageFormatError from taiga.base import exceptions as exc from taiga.base.utils.urls import get_absolute_url +from taiga.projects.notifications.choices import NotifyLevel from .gravatar import get_gravatar_url @@ -179,6 +181,50 @@ def get_watched_content_for_user(user): return user_watches +def _build_favourites_sql_for_projects(for_user): + sql = """ + SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'watch' AS action, + tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project, + slug AS slug, projects_project.name AS subject, + notifications_notifypolicy.created_at, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to + FROM notifications_notifypolicy + INNER JOIN projects_project + ON (projects_project.id = notifications_notifypolicy.project_id) + LEFT JOIN (SELECT project_id, count(*) watchers + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.notify_level != {ignore_notify_level} + GROUP BY project_id + ) type_watchers + ON projects_project.id = type_watchers.project_id + LEFT JOIN votes_votes + ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) + WHERE notifications_notifypolicy.user_id = {for_user_id} + UNION + SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'vote' AS action, + tags, votes_vote.object_id AS object_id, projects_project.id AS project, + slug AS slug, projects_project.name AS subject, + votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to + FROM votes_vote + INNER JOIN projects_project + ON (projects_project.id = votes_vote.object_id) + LEFT JOIN (SELECT project_id, count(*) watchers + FROM notifications_notifypolicy + WHERE notifications_notifypolicy.notify_level != {ignore_notify_level} + GROUP BY project_id + ) type_watchers + ON projects_project.id = type_watchers.project_id + LEFT JOIN votes_votes + ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) + WHERE votes_vote.user_id = {for_user_id} + """ + sql = sql.format( + for_user_id=for_user.id, + ignore_notify_level=NotifyLevel.ignore, + project_content_type_id=ContentType.objects.get(app_label="projects", model="project").id) + return sql + + + def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", project_column="project_id", assigned_to_column="assigned_to_id", slug_column="slug", subject_column="subject"): @@ -217,6 +263,7 @@ def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", ref_column = ref_column, project_column=project_column, assigned_to_column=assigned_to_column, slug_column=slug_column, subject_column=subject_column) + return sql @@ -298,12 +345,7 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): userstories_sql=_build_favourites_sql_for_type(for_user, "userstory", "userstories_userstory", slug_column="null"), tasks_sql=_build_favourites_sql_for_type(for_user, "task", "tasks_task", slug_column="null"), issues_sql=_build_favourites_sql_for_type(for_user, "issue", "issues_issue", slug_column="null"), - projects_sql=_build_favourites_sql_for_type(for_user, "project", "projects_project", - ref_column="null", - project_column="id", - assigned_to_column="null", - subject_column="projects_project.name") - ) + projects_sql=_build_favourites_sql_for_projects(for_user)) cursor = connection.cursor() cursor.execute(sql) diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index c76fa68f..26132062 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -81,13 +81,6 @@ def data(): f.VotesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2) f.VotesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2) - f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_member_with_perms) - f.WatchedFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_owner) - f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_member_with_perms) - f.WatchedFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_owner) - f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_member_with_perms) - f.WatchedFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_owner) - return m @@ -322,11 +315,11 @@ def test_project_watchers_list(client, data): ] results = helper_test_http_method_and_count(client, 'get', public_url, None, users) - assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)] + assert results == [(200, 1), (200, 1), (200, 1), (200, 1), (200, 1)] results = helper_test_http_method_and_count(client, 'get', private1_url, None, users) - assert results == [(200, 2), (200, 2), (200, 2), (200, 2), (200, 2)] + assert results == [(200, 3), (200, 3), (200, 3), (200, 3), (200, 3)] results = helper_test_http_method_and_count(client, 'get', private2_url, None, users) - assert results == [(401, 0), (403, 0), (403, 0), (200, 2), (200, 2)] + assert results == [(401, 0), (403, 0), (403, 0), (200, 3), (200, 3)] def test_project_watchers_retrieve(client, data): diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index 073d7585..a0f2219b 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -68,7 +68,7 @@ def test_valid_project_import_without_extra_data(client): ] assert all(map(lambda x: len(response_data[x]) == 0, must_empty_children)) assert response_data["owner"] == user.email - assert response_data["watchers"] == [user_watching.email] + assert response_data["watchers"] == [user.email, user_watching.email] def test_valid_project_import_with_not_existing_memberships(client): diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 183a581a..4442ad92 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -32,6 +32,7 @@ from .. import factories as f from taiga.base.utils import json from taiga.projects.notifications import services +from taiga.projects.notifications import utils from taiga.projects.notifications import models from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.history.choices import HistoryType @@ -56,7 +57,7 @@ def test_attach_notify_level_to_project_queryset(): f.ProjectFactory.create() qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) + qs = utils.attach_notify_level_to_project_queryset(qs, project1.owner) assert len(qs) == 2 assert qs[0].notify_level == NotifyLevel.notwatch @@ -64,7 +65,7 @@ def test_attach_notify_level_to_project_queryset(): services.create_notify_policy(project1, project1.owner, NotifyLevel.watch) qs = project1.__class__.objects.order_by("id") - qs = services.attach_notify_level_to_project_queryset(qs, project1.owner) + qs = utils.attach_notify_level_to_project_queryset(qs, project1.owner) assert qs[0].notify_level == NotifyLevel.watch assert qs[1].notify_level == NotifyLevel.notwatch @@ -143,10 +144,25 @@ def test_users_to_notify(): role2 = f.RoleFactory.create(project=project, permissions=[]) member1 = f.MembershipFactory.create(project=project, role=role1) + policy_member1 = member1.user.notify_policies.get(project=project) + policy_member1.notify_level = NotifyLevel.ignore + policy_member1.save() member2 = f.MembershipFactory.create(project=project, role=role1) + policy_member2 = member2.user.notify_policies.get(project=project) + policy_member2.notify_level = NotifyLevel.ignore + policy_member2.save() member3 = f.MembershipFactory.create(project=project, role=role1) + policy_member3 = member3.user.notify_policies.get(project=project) + policy_member3.notify_level = NotifyLevel.ignore + policy_member3.save() member4 = f.MembershipFactory.create(project=project, role=role1) + policy_member4 = member4.user.notify_policies.get(project=project) + policy_member4.notify_level = NotifyLevel.ignore + policy_member4.save() member5 = f.MembershipFactory.create(project=project, role=role2) + policy_member5 = member5.user.notify_policies.get(project=project) + policy_member5.notify_level = NotifyLevel.ignore + policy_member5.save() inactive_member1 = f.MembershipFactory.create(project=project, role=role1) inactive_member1.user.is_active = False inactive_member1.user.save() @@ -158,14 +174,13 @@ def test_users_to_notify(): policy_model_cls = apps.get_model("notifications", "NotifyPolicy") - policy1 = policy_model_cls.objects.get(user=member1.user) - policy2 = policy_model_cls.objects.get(user=member3.user) - policy3 = policy_model_cls.objects.get(user=inactive_member1.user) - policy3.notify_level = NotifyLevel.watch - policy3.save() - policy4 = policy_model_cls.objects.get(user=system_member1.user) - policy4.notify_level = NotifyLevel.watch - policy4.save() + policy_inactive_member1 = policy_model_cls.objects.get(user=inactive_member1.user) + policy_inactive_member1.notify_level = NotifyLevel.watch + policy_inactive_member1.save() + + policy_system_member1 = policy_model_cls.objects.get(user=system_member1.user) + policy_system_member1.notify_level = NotifyLevel.watch + policy_system_member1.save() history = MagicMock() history.owner = member2.user @@ -174,13 +189,15 @@ def test_users_to_notify(): # Test basic description modifications issue.description = "test1" issue.save() + policy_member4.notify_level = NotifyLevel.watch + policy_member4.save() users = services.get_users_to_notify(issue) assert len(users) == 1 assert tuple(users)[0] == issue.get_owner() # Test watch notify level in one member - policy1.notify_level = NotifyLevel.watch - policy1.save() + policy_member1.notify_level = NotifyLevel.watch + policy_member1.save() users = services.get_users_to_notify(issue) assert len(users) == 2 @@ -188,13 +205,15 @@ def test_users_to_notify(): # Test with watchers issue.add_watcher(member3.user) + policy_member3.notify_level = NotifyLevel.watch + policy_member3.save() users = services.get_users_to_notify(issue) assert len(users) == 3 assert users == {member1.user, member3.user, issue.get_owner()} # Test with watchers with ignore policy - policy2.notify_level = NotifyLevel.ignore - policy2.save() + policy_member3.notify_level = NotifyLevel.ignore + policy_member3.save() issue.add_watcher(member3.user) users = services.get_users_to_notify(issue) diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 4620e881..582c3667 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -200,6 +200,7 @@ def test_update_project_timeline(): project = factories.ProjectFactory.create(name="test project timeline") history_services.take_snapshot(project, user=project.owner) project.add_watcher(user_watcher) + print("PPPP") project.name = "test project timeline updated" project.save() history_services.take_snapshot(project, user=project.owner) @@ -341,7 +342,7 @@ def test_update_membership_timeline(): def test_delete_project_timeline(): project = factories.ProjectFactory.create(name="test project timeline") user_watcher= factories.UserFactory() - project.add_watcher(user_watcher) + project.add_watcher(user_watcher) history_services.take_snapshot(project, user=project.owner, delete=True) user_timeline = service.get_project_timeline(project) assert user_timeline[0].event_type == "projects.project.delete" @@ -354,7 +355,7 @@ def test_delete_project_timeline(): def test_delete_milestone_timeline(): milestone = factories.MilestoneFactory.create(name="test milestone timeline") user_watcher= factories.UserFactory() - milestone.add_watcher(user_watcher) + milestone.add_watcher(user_watcher) history_services.take_snapshot(milestone, user=milestone.owner, delete=True) project_timeline = service.get_project_timeline(milestone.project) assert project_timeline[0].event_type == "milestones.milestone.delete" @@ -367,7 +368,7 @@ def test_delete_milestone_timeline(): def test_delete_user_story_timeline(): user_story = factories.UserStoryFactory.create(subject="test us timeline") user_watcher= factories.UserFactory() - user_story.add_watcher(user_watcher) + user_story.add_watcher(user_watcher) history_services.take_snapshot(user_story, user=user_story.owner, delete=True) project_timeline = service.get_project_timeline(user_story.project) assert project_timeline[0].event_type == "userstories.userstory.delete" @@ -380,7 +381,7 @@ def test_delete_user_story_timeline(): def test_delete_issue_timeline(): issue = factories.IssueFactory.create(subject="test issue timeline") user_watcher= factories.UserFactory() - issue.add_watcher(user_watcher) + issue.add_watcher(user_watcher) history_services.take_snapshot(issue, user=issue.owner, delete=True) project_timeline = service.get_project_timeline(issue.project) assert project_timeline[0].event_type == "issues.issue.delete" @@ -393,7 +394,7 @@ def test_delete_issue_timeline(): def test_delete_task_timeline(): task = factories.TaskFactory.create(subject="test task timeline") user_watcher= factories.UserFactory() - task.add_watcher(user_watcher) + task.add_watcher(user_watcher) history_services.take_snapshot(task, user=task.owner, delete=True) project_timeline = service.get_project_timeline(task.project) assert project_timeline[0].event_type == "tasks.task.delete" @@ -406,7 +407,7 @@ def test_delete_task_timeline(): def test_delete_wiki_page_timeline(): page = factories.WikiPageFactory.create(slug="test wiki page timeline") user_watcher= factories.UserFactory() - page.add_watcher(user_watcher) + page.add_watcher(user_watcher) history_services.take_snapshot(page, user=page.owner, delete=True) project_timeline = service.get_project_timeline(page.project) assert project_timeline[0].event_type == "wiki.wikipage.delete" diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 5d643eb4..bbf58a0c 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -19,6 +19,8 @@ import pytest import json from django.core.urlresolvers import reverse +from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS + from .. import factories as f pytestmark = pytest.mark.django_db @@ -124,8 +126,10 @@ def test_get_project_watchers(client): def test_get_project_is_watched(client): user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) + project = f.ProjectFactory.create(is_private=False, + anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), + public_permissions=list(map(lambda x: x[0], USER_PERMISSIONS))) + url_detail = reverse("projects-detail", args=(project.id,)) url_watch = reverse("projects-watch", args=(project.id,)) url_unwatch = reverse("projects-unwatch", args=(project.id,)) @@ -133,6 +137,7 @@ def test_get_project_is_watched(client): client.login(user) response = client.get(url_detail) + assert response.status_code == 200 assert response.data['watchers'] == [] assert response.data['is_watched'] == False From bd09e23b611c48563c7b02bdf93d4adacbae19fd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 1 Sep 2015 12:38:39 +0200 Subject: [PATCH 123/190] External applications support --- CHANGELOG.md | 1 + requirements.txt | 1 + settings/common.py | 4 + taiga/base/api/__init__.py | 4 +- taiga/base/api/viewsets.py | 4 + taiga/external_apps/__init__.py | 0 taiga/external_apps/admin.py | 32 ++++ taiga/external_apps/api.py | 104 +++++++++++++ taiga/external_apps/auth_backends.py | 40 +++++ taiga/external_apps/encryption.py | 30 ++++ .../external_apps/migrations/0001_initial.py | 52 +++++++ taiga/external_apps/migrations/__init__.py | 0 taiga/external_apps/models.py | 85 ++++++++++ taiga/external_apps/permissions.py | 43 ++++++ taiga/external_apps/serializers.py | 56 +++++++ taiga/external_apps/services.py | 54 +++++++ taiga/routers.py | 7 + tests/factories.py | 15 ++ .../test_application_tokens_resources.py | 145 ++++++++++++++++++ tests/integration/test_application_tokens.py | 102 ++++++++++++ 20 files changed, 778 insertions(+), 1 deletion(-) create mode 100644 taiga/external_apps/__init__.py create mode 100644 taiga/external_apps/admin.py create mode 100644 taiga/external_apps/api.py create mode 100644 taiga/external_apps/auth_backends.py create mode 100644 taiga/external_apps/encryption.py create mode 100644 taiga/external_apps/migrations/0001_initial.py create mode 100644 taiga/external_apps/migrations/__init__.py create mode 100644 taiga/external_apps/models.py create mode 100644 taiga/external_apps/permissions.py create mode 100644 taiga/external_apps/serializers.py create mode 100644 taiga/external_apps/services.py create mode 100644 tests/integration/resources_permissions/test_application_tokens_resources.py create mode 100644 tests/integration/test_application_tokens.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 55f931f2..1dc96593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - Now users can watch public issues, tasks and user stories. - Add endpoints to show the watchers list for issues, tasks and user stories. - Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). +- Add externall apps: now Taiga can integrate with hundreds of applications and service. - i18n. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. diff --git a/requirements.txt b/requirements.txt index d9016d71..946cf442 100644 --- a/requirements.txt +++ b/requirements.txt @@ -31,6 +31,7 @@ premailer==2.8.1 django-transactional-cleanup==0.1.14 lxml==3.4.1 git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea +pyjwkest==1.0.3 # Comment it if you are using python >= 3.4 enum34==1.0 diff --git a/settings/common.py b/settings/common.py index c32f5cdb..644fb501 100644 --- a/settings/common.py +++ b/settings/common.py @@ -266,6 +266,7 @@ INSTALLED_APPS = [ "taiga.front", "taiga.users", "taiga.userstorage", + "taiga.external_apps", "taiga.projects", "taiga.projects.references", "taiga.projects.custom_attributes", @@ -384,6 +385,9 @@ REST_FRAMEWORK = { # Mainly used for api debug. "taiga.auth.backends.Session", + + # Application tokens auth + "taiga.external_apps.auth_backends.Token", ), "DEFAULT_THROTTLE_CLASSES": ( "taiga.base.throttling.AnonRateThrottle", diff --git a/taiga/base/api/__init__.py b/taiga/base/api/__init__.py index 69083fa5..8b2b6ad6 100644 --- a/taiga/base/api/__init__.py +++ b/taiga/base/api/__init__.py @@ -31,9 +31,11 @@ from .viewsets import ModelCrudViewSet from .viewsets import ModelUpdateRetrieveViewSet from .viewsets import GenericViewSet from .viewsets import ReadOnlyListViewSet +from .viewsets import ModelRetrieveViewSet __all__ = ["ModelCrudViewSet", "ModelListViewSet", "ModelUpdateRetrieveViewSet", "GenericViewSet", - "ReadOnlyListViewSet"] + "ReadOnlyListViewSet", + "ModelRetrieveViewSet"] diff --git a/taiga/base/api/viewsets.py b/taiga/base/api/viewsets.py index dd56cd14..e587d4ab 100644 --- a/taiga/base/api/viewsets.py +++ b/taiga/base/api/viewsets.py @@ -167,3 +167,7 @@ class ModelUpdateRetrieveViewSet(mixins.UpdateModelMixin, mixins.RetrieveModelMixin, GenericViewSet): pass + +class ModelRetrieveViewSet(mixins.RetrieveModelMixin, + GenericViewSet): + pass diff --git a/taiga/external_apps/__init__.py b/taiga/external_apps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/external_apps/admin.py b/taiga/external_apps/admin.py new file mode 100644 index 00000000..62efaafd --- /dev/null +++ b/taiga/external_apps/admin.py @@ -0,0 +1,32 @@ +# 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 django.contrib import admin + +from . import models + + +class ApplicationAdmin(admin.ModelAdmin): + readonly_fields=("id",) + +admin.site.register(models.Application, ApplicationAdmin) + + +class ApplicationTokenAdmin(admin.ModelAdmin): + readonly_fields=("token",) + search_fields = ("user__username", "user__full_name", "user__email", "application__name") + +admin.site.register(models.ApplicationToken, ApplicationTokenAdmin) diff --git a/taiga/external_apps/api.py b/taiga/external_apps/api.py new file mode 100644 index 00000000..092a9dfd --- /dev/null +++ b/taiga/external_apps/api.py @@ -0,0 +1,104 @@ +# 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 . import serializers +from . import models +from . import permissions +from . import services + +from taiga.base import response +from taiga.base import exceptions as exc +from taiga.base.api import ModelCrudViewSet, ModelRetrieveViewSet +from taiga.base.api.utils import get_object_or_404 +from taiga.base.decorators import list_route, detail_route + +from django.db import transaction +from django.utils.translation import ugettext_lazy as _ + + +class Application(ModelRetrieveViewSet): + serializer_class = serializers.ApplicationSerializer + permission_classes = (permissions.ApplicationPermission,) + model = models.Application + + @detail_route(methods=["GET"]) + def token(self, request, *args, **kwargs): + if self.request.user.is_anonymous(): + raise exc.NotAuthenticated(_("Authentication required")) + + application = get_object_or_404(models.Application, **kwargs) + self.check_permissions(request, 'token', request.user) + try: + application_token = models.ApplicationToken.objects.get(user=request.user, application=application) + application_token.update_auth_code() + application_token.state = request.GET.get("state", None) + application_token.save() + + except models.ApplicationToken.DoesNotExist: + application_token = models.ApplicationToken( + user=request.user, + application=application + ) + + auth_code_data = serializers.ApplicationTokenSerializer(application_token).data + return response.Ok(auth_code_data) + + +class ApplicationToken(ModelCrudViewSet): + serializer_class = serializers.ApplicationTokenSerializer + permission_classes = (permissions.ApplicationTokenPermission,) + + def get_queryset(self): + if self.request.user.is_anonymous(): + raise exc.NotAuthenticated(_("Authentication required")) + + return models.ApplicationToken.objects.filter(user=self.request.user) + + @list_route(methods=["POST"]) + def authorize(self, request, pk=None): + if self.request.user.is_anonymous(): + raise exc.NotAuthenticated(_("Authentication required")) + + application_id = request.DATA.get("application", None) + state = request.DATA.get("state", None) + application_token = services.authorize_token(application_id, request.user, state) + + auth_code_data = serializers.AuthorizationCodeSerializer(application_token).data + return response.Ok(auth_code_data) + + @list_route(methods=["POST"]) + def validate(self, request, pk=None): + application_id = request.DATA.get("application", None) + auth_code = request.DATA.get("auth_code", None) + state = request.DATA.get("state", None) + application_token = get_object_or_404(models.ApplicationToken, + application__id=application_id, + auth_code=auth_code, + state=state) + + application_token.generate_token() + application_token.save() + + access_token_data = serializers.AccessTokenSerializer(application_token).data + return response.Ok(access_token_data) + + # POST method disabled + def create(self, *args, **kwargs): + raise exc.NotSupported() + + # PATCH and PUT methods disabled + def update(self, *args, **kwargs): + raise exc.NotSupported() diff --git a/taiga/external_apps/auth_backends.py b/taiga/external_apps/auth_backends.py new file mode 100644 index 00000000..ff86e70e --- /dev/null +++ b/taiga/external_apps/auth_backends.py @@ -0,0 +1,40 @@ +# 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 . + +import re + +from taiga.base.api.authentication import BaseAuthentication + +from . import services + +class Token(BaseAuthentication): + auth_rx = re.compile(r"^Application (.+)$") + + def authenticate(self, request): + if "HTTP_AUTHORIZATION" not in request.META: + return None + + token_rx_match = self.auth_rx.search(request.META["HTTP_AUTHORIZATION"]) + if not token_rx_match: + return None + + token = token_rx_match.group(1) + user = services.get_user_for_application_token(token) + + return (user, token) + + def authenticate_header(self, request): + return 'Bearer realm="api"' diff --git a/taiga/external_apps/encryption.py b/taiga/external_apps/encryption.py new file mode 100644 index 00000000..1df1bda7 --- /dev/null +++ b/taiga/external_apps/encryption.py @@ -0,0 +1,30 @@ +# 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 Crypto.PublicKey import RSA +from jwkest.jwk import SYMKey +from jwkest.jwe import JWE + + +def encrypt(content, key): + sym_key = SYMKey(key=key, alg="A128KW") + jwe = JWE(content, alg="A128KW", enc="A256GCM") + return jwe.encrypt([sym_key]) + + +def decrypt(content, key): + sym_key = SYMKey(key=key, alg="A128KW") + return JWE().decrypt(content, keys=[sym_key]) diff --git a/taiga/external_apps/migrations/0001_initial.py b/taiga/external_apps/migrations/0001_initial.py new file mode 100644 index 00000000..db76cc21 --- /dev/null +++ b/taiga/external_apps/migrations/0001_initial.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings +import taiga.external_apps.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('id', models.CharField(serialize=False, unique=True, max_length=255, default=taiga.external_apps.models._generate_uuid, primary_key=True)), + ('name', models.CharField(verbose_name='name', max_length=255)), + ('icon_url', models.TextField(null=True, blank=True, verbose_name='Icon url')), + ('web', models.CharField(null=True, blank=True, max_length=255, verbose_name='web')), + ('description', models.TextField(null=True, blank=True, verbose_name='description')), + ('next_url', models.TextField(verbose_name='Next url')), + ('key', models.TextField(verbose_name='secret key for cyphering the application tokens')), + ], + options={ + 'verbose_name_plural': 'applications', + 'verbose_name': 'application', + 'ordering': ['name'], + }, + bases=(models.Model,), + ), + migrations.CreateModel( + name='ApplicationToken', + fields=[ + ('id', models.AutoField(serialize=False, auto_created=True, verbose_name='ID', primary_key=True)), + ('auth_code', models.CharField(null=True, blank=True, max_length=255, default=None)), + ('token', models.CharField(null=True, blank=True, max_length=255, default=None)), + ('state', models.CharField(null=True, blank=True, max_length=255, default='')), + ('application', models.ForeignKey(verbose_name='application', related_name='application_tokens', to='external_apps.Application')), + ('user', models.ForeignKey(verbose_name='user', related_name='application_tokens', to=settings.AUTH_USER_MODEL)), + ], + options={ + }, + bases=(models.Model,), + ), + migrations.AlterUniqueTogether( + name='applicationtoken', + unique_together=set([('application', 'user')]), + ), + ] diff --git a/taiga/external_apps/migrations/__init__.py b/taiga/external_apps/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/external_apps/models.py b/taiga/external_apps/models.py new file mode 100644 index 00000000..0e3fcd8d --- /dev/null +++ b/taiga/external_apps/models.py @@ -0,0 +1,85 @@ +# 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 django.conf import settings +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from . import services + +import uuid + +def _generate_uuid(): + return str(uuid.uuid1()) + + +class Application(models.Model): + id = models.CharField(primary_key=True, max_length=255, unique=True, default=_generate_uuid) + + name = models.CharField(max_length=255, null=False, blank=False, + verbose_name=_("name")) + + icon_url = models.TextField(null=True, blank=True, verbose_name=_("Icon url")) + web = models.CharField(max_length=255, null=True, blank=True, verbose_name=_("web")) + description = models.TextField(null=True, blank=True, verbose_name=_("description")) + + next_url = models.TextField(null=False, blank=False, verbose_name=_("Next url")) + + key = models.TextField(null=False, blank=False, verbose_name=_("secret key for cyphering the application tokens")) + + class Meta: + verbose_name = "application" + verbose_name_plural = "applications" + ordering = ["name"] + + def __str__(self): + return self.name + + +class ApplicationToken(models.Model): + user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False, + related_name="application_tokens", + verbose_name=_("user")) + + application = models.ForeignKey("Application", blank=False, null=False, + related_name="application_tokens", + verbose_name=_("application")) + + auth_code = models.CharField(max_length=255, null=True, blank=True, default=None) + token = models.CharField(max_length=255, null=True, blank=True, default=None) + # An unguessable random string. It is used to protect against cross-site request forgery attacks. + state = models.CharField(max_length=255, null=True, blank=True, default="") + + class Meta: + unique_together = ("application", "user",) + + def __str__(self): + return "{application}: {user} - {token}".format(application=self.application.name, user=self.user.get_full_name(), token=self.token) + + @property + def cyphered_token(self): + return services.cypher_token(self) + + @property + def next_url(self): + return "{url}?auth_code={auth_code}".format(url=self.application.next_url, auth_code=self.auth_code) + + def update_auth_code(self): + self.auth_code = _generate_uuid() + + def generate_token(self): + self.auth_code = None + self.token = _generate_uuid() diff --git a/taiga/external_apps/permissions.py b/taiga/external_apps/permissions.py new file mode 100644 index 00000000..88604e23 --- /dev/null +++ b/taiga/external_apps/permissions.py @@ -0,0 +1,43 @@ +# 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.base.api.permissions import TaigaResourcePermission +from taiga.base.api.permissions import IsAuthenticated +from taiga.base.api.permissions import PermissionComponent + + +class ApplicationPermission(TaigaResourcePermission): + retrieve_perms = IsAuthenticated() + token_perms = IsAuthenticated() + list_perms = IsAuthenticated() + + +class CanUseToken(PermissionComponent): + def check_permissions(self, request, view, obj=None): + if not obj: + return False + + return request.user == obj.user + + +class ApplicationTokenPermission(TaigaResourcePermission): + retrieve_perms = IsAuthenticated() & CanUseToken() + by_application_perms = IsAuthenticated() + create_perms = IsAuthenticated() + update_perms = IsAuthenticated() & CanUseToken() + partial_update_perms = IsAuthenticated() & CanUseToken() + destroy_perms = IsAuthenticated() & CanUseToken() + list_perms = IsAuthenticated() diff --git a/taiga/external_apps/serializers.py b/taiga/external_apps/serializers.py new file mode 100644 index 00000000..fe472d8c --- /dev/null +++ b/taiga/external_apps/serializers.py @@ -0,0 +1,56 @@ +# 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 . + +import json + +from taiga.base.api import serializers + +from . import models +from . import services + +from django.utils.translation import ugettext as _ + + +class ApplicationSerializer(serializers.ModelSerializer): + class Meta: + model = models.Application + fields = ("id", "name", "web", "description", "icon_url") + + +class ApplicationTokenSerializer(serializers.ModelSerializer): + cyphered_token = serializers.CharField(source="cyphered_token", read_only=True) + next_url = serializers.CharField(source="next_url", read_only=True) + application = ApplicationSerializer(read_only=True) + + class Meta: + model = models.ApplicationToken + fields = ("user", "id", "application", "auth_code", "next_url") + + +class AuthorizationCodeSerializer(serializers.ModelSerializer): + next_url = serializers.CharField(source="next_url", read_only=True) + class Meta: + model = models.ApplicationToken + fields = ("auth_code", "state", "next_url") + + +class AccessTokenSerializer(serializers.ModelSerializer): + cyphered_token = serializers.CharField(source="cyphered_token", read_only=True) + next_url = serializers.CharField(source="next_url", read_only=True) + + class Meta: + model = models.ApplicationToken + fields = ("cyphered_token", ) diff --git a/taiga/external_apps/services.py b/taiga/external_apps/services.py new file mode 100644 index 00000000..14ae8b73 --- /dev/null +++ b/taiga/external_apps/services.py @@ -0,0 +1,54 @@ +# 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.base import exceptions as exc +from taiga.base.api.utils import get_object_or_404 + +from django.apps import apps +from django.utils.translation import ugettext as _ + +from . import encryption + +import json + +def get_user_for_application_token(token:str) -> object: + """ + Given an application token it tries to find an associated user + """ + app_token = apps.get_model("external_apps", "ApplicationToken").objects.filter(token=token).first() + if not app_token: + raise exc.NotAuthenticated(_("Invalid token")) + return app_token.user + + +def authorize_token(application_id:int, user:object, state:str) -> object: + ApplicationToken = apps.get_model("external_apps", "ApplicationToken") + Application = apps.get_model("external_apps", "Application") + application = get_object_or_404(Application, id=application_id) + token, _ = ApplicationToken.objects.get_or_create(user=user, application=application) + token.update_auth_code() + token.state = state + token.save() + return token + + +def cypher_token(application_token:object) -> str: + content = { + "token": application_token.token + } + + return encryption.encrypt(json.dumps(content), application_token.application.key) diff --git a/taiga/routers.py b/taiga/routers.py index 5a587b59..be507983 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -213,6 +213,13 @@ router.register(r"importer", ProjectImporterViewSet, base_name="importer") router.register(r"exporter", ProjectExporterViewSet, base_name="exporter") +# External apps +from taiga.external_apps.api import Application, ApplicationToken +router.register(r"applications", Application, base_name="applications") +router.register(r"application-tokens", ApplicationToken, base_name="application-tokens") + + + # Stats # - see taiga.stats.routers and taiga.stats.apps diff --git a/tests/factories.py b/tests/factories.py index a0950733..2cb676b5 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -482,6 +482,21 @@ class HistoryEntryFactory(Factory): type = 1 +class ApplicationFactory(Factory): + class Meta: + model = "external_apps.Application" + strategy = factory.CREATE_STRATEGY + + key = "testingkey" + +class ApplicationTokenFactory(Factory): + class Meta: + model = "external_apps.ApplicationToken" + strategy = factory.CREATE_STRATEGY + + application = factory.SubFactory("tests.factories.ApplicationFactory") + user = factory.SubFactory("tests.factories.UserFactory") + def create_issue(**kwargs): "Create an issue and along with its dependencies." owner = kwargs.pop("owner", None) diff --git a/tests/integration/resources_permissions/test_application_tokens_resources.py b/tests/integration/resources_permissions/test_application_tokens_resources.py new file mode 100644 index 00000000..cc4e3a5d --- /dev/null +++ b/tests/integration/resources_permissions/test_application_tokens_resources.py @@ -0,0 +1,145 @@ +from django.core.urlresolvers import reverse + +from taiga.base.utils import json +from tests import factories as f +from tests.utils import helper_test_http_method, disconnect_signals, reconnect_signals + +from unittest import mock + +import pytest +pytestmark = pytest.mark.django_db + + +def setup_module(module): + disconnect_signals() + + +def teardown_module(module): + reconnect_signals() + + +@pytest.fixture +def data(): + m = type("Models", (object,), {}) + m.registered_user = f.UserFactory.create() + m.token = f.ApplicationTokenFactory(state="random-state") + m.registered_user_with_token = m.token.user + return m + + +def test_application_tokens_create(client, data): + url = reverse('application-tokens-list') + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + data = json.dumps({"application": data.token.application.id}) + results = helper_test_http_method(client, "post", url, data, users) + assert results == [405, 405, 405] + + +def test_applications_retrieve_token(client, data): + url=reverse('applications-token', kwargs={"pk": data.token.application.id}) + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + results = helper_test_http_method(client, "get", url, None, users) + assert results == [401, 200, 200] + + +def test_application_tokens_retrieve(client, data): + url = reverse('application-tokens-detail', kwargs={"pk": data.token.id}) + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + results = helper_test_http_method(client, "get", url, None, users) + assert results == [401, 404, 200] + + +def test_application_tokens_authorize(client, data): + url=reverse('application-tokens-authorize') + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + data = json.dumps({ + "application": data.token.application.id, + "state": "random-state-123123", + }) + + results = helper_test_http_method(client, "post", url, data, users) + assert results == [401, 200, 200] + + +def test_application_tokens_validate(client, data): + url=reverse('application-tokens-validate') + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + data = json.dumps({ + "application": data.token.application.id, + "key": data.token.application.key, + "auth_code": data.token.auth_code, + "state": data.token.state + }) + + results = helper_test_http_method(client, "post", url, data, users) + assert results == [200, 200, 200] + + +def test_application_tokens_update(client, data): + url = reverse('application-tokens-detail', kwargs={"pk": data.token.id}) + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + patch_data = json.dumps({"application": data.token.application.id}) + results = helper_test_http_method(client, "patch", url, patch_data, users) + assert results == [405, 405, 405] + + +def test_application_tokens_delete(client, data): + url = reverse('application-tokens-detail', kwargs={"pk": data.token.id}) + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + results = helper_test_http_method(client, "delete", url, None, users) + assert results == [401, 403, 204] + + +def test_application_tokens_list(client, data): + url = reverse('application-tokens-list') + + users = [ + None, + data.registered_user, + data.registered_user_with_token + ] + + results = helper_test_http_method(client, "get", url, None, users) + assert results == [401, 200, 200] diff --git a/tests/integration/test_application_tokens.py b/tests/integration/test_application_tokens.py new file mode 100644 index 00000000..da6d986e --- /dev/null +++ b/tests/integration/test_application_tokens.py @@ -0,0 +1,102 @@ +from django.core.urlresolvers import reverse + +from taiga.external_apps import encryption +from taiga.external_apps import models + + +from .. import factories as f + +import json +import pytest +pytestmark = pytest.mark.django_db + + +def test_own_tokens_listing(client): + user_1 = f.UserFactory.create() + user_2 = f.UserFactory.create() + token_1 = f.ApplicationTokenFactory(user=user_1) + token_2 = f.ApplicationTokenFactory(user=user_2) + url = reverse("application-tokens-list") + client.login(user_1) + response = client.json.get(url) + assert response.status_code == 200 + assert len(response.data) == 1 + assert response.data[0].get("id") == token_1.id + assert response.data[0].get("application").get("id") == token_1.application.id + + +def test_retrieve_existing_token_for_application(client): + token = f.ApplicationTokenFactory() + url = reverse("applications-token", args=[token.application.id]) + client.login(token.user) + response = client.json.get(url) + assert response.status_code == 200 + assert response.data.get("application").get("id") == token.application.id + + + +def test_retrieve_unexisting_token_for_application(client): + user = f.UserFactory.create() + url = reverse("applications-token", args=[-1]) + client.login(user) + response = client.json.get(url) + assert response.status_code == 404 + + +def test_token_authorize(client): + user = f.UserFactory.create() + application = f.ApplicationFactory() + url = reverse("application-tokens-authorize") + client.login(user) + + data = json.dumps({ + "application": application.id, + "state": "random-state" + }) + + response = client.json.post(url, data) + + assert response.status_code == 200 + assert response.data["state"] == "random-state" + auth_code_1 = response.data["auth_code"] + + response = client.json.post(url, data) + assert response.status_code == 200 + assert response.data["state"] == "random-state" + auth_code_2 = response.data["auth_code"] + assert auth_code_1 != auth_code_2 + + +def test_token_authorize_invalid_app(client): + user = f.UserFactory.create() + url = reverse("application-tokens-authorize") + client.login(user) + + data = json.dumps({ + "application": 33, + "state": "random-state" + }) + + response = client.json.post(url, data) + assert response.status_code == 404 + + +def test_token_validate(client): + user = f.UserFactory.create() + application = f.ApplicationFactory(next_url="http://next.url") + token = f.ApplicationTokenFactory(auth_code="test-auth-code", state="test-state", application=application) + url = reverse("application-tokens-validate") + client.login(user) + + data = { + "application": token.application.id, + "auth_code": "test-auth-code", + "state": "test-state" + } + response = client.json.post(url, json.dumps(data)) + assert response.status_code == 200 + + token = models.ApplicationToken.objects.get(id=token.id) + decyphered_token = encryption.decrypt(response.data["cyphered_token"], token.application.key)[0] + decyphered_token = json.loads(decyphered_token.decode("utf-8")) + assert decyphered_token["token"] == token.token From 77c470f062d827b84eb3ca50321a923e6fcc9d83 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 9 Sep 2015 12:24:16 +0200 Subject: [PATCH 124/190] Fixing watchers deletion when updating userstories, tasks or issues --- taiga/projects/notifications/mixins.py | 13 +++++++++++-- tests/integration/test_userstories.py | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index e6e6fc74..bf42a9ed 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -191,7 +191,11 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): watcher_field = self.fields.pop("watchers", None) instance = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) if instance is not None and self.validate_watchers(attrs, "watchers"): - new_watcher_ids = set(attrs.get("watchers", [])) + #A partial update can exclude the watchers field + if not "watchers" in attrs: + return instance + + new_watcher_ids = set(attrs.get("watchers", None)) old_watcher_ids = set(instance.get_watchers().values_list("id", flat=True)) adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) @@ -209,7 +213,6 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): return instance - def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it if obj is not None and not hasattr(obj, "watchers"): @@ -217,6 +220,12 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): return super(WatchedResourceModelSerializer, self).to_native(obj) + def save(self, **kwargs): + obj = super(WatchedResourceModelSerializer, self).save(**kwargs) + self.fields["watchers"] = WatchersField(required=False) + obj.watchers = [user.id for user in obj.get_watchers()] + return obj + class WatchersViewSetMixin: # Is a ModelListViewSet with two required params: permission_classes and resource_model diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 33b18a8d..f90fd97a 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -443,3 +443,21 @@ def test_custom_fields_csv_generation(): assert row[26] == attr.name row = next(reader) assert row[26] == "val1" + + +def test_update_userstory_respecting_watchers(client): + watching_user = f.create_user() + project = f.ProjectFactory.create() + us = f.UserStoryFactory.create(project=project, status__project=project, milestone__project=project) + us.add_watcher(watching_user) + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) + f.MembershipFactory.create(project=us.project, user=watching_user) + + client.login(user=us.owner) + url = reverse("userstories-detail", kwargs={"pk": us.pk}) + data = {"subject": "Updating test", "version": 1} + + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data["subject"] == "Updating test" + assert response.data["watchers"] == [watching_user.id] From 86436ef1f2b8192bc598b578050cc1fa38b7193f Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 10 Sep 2015 09:24:18 +0200 Subject: [PATCH 125/190] Fixing import --- taiga/export_import/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index 01f042cf..9ea19705 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -259,7 +259,7 @@ class WatcheableObjectModelSerializer(serializers.ModelSerializer): for user in removing_users: notifications_services.remove_watcher(self.object, user) - self.object.watchers = self.object.get_watchers() + self.object.watchers = [user.email for user in self.object.get_watchers()] def to_native(self, obj): ret = super(WatcheableObjectModelSerializer, self).to_native(obj) From 690714eadcccb5dc4926a2077fd8132f1adea7b6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 10 Sep 2015 09:49:36 +0200 Subject: [PATCH 126/190] Fixing activiy for removed users --- taiga/projects/history/models.py | 5 ++++- taiga/projects/history/serializers.py | 11 ++++++----- taiga/users/services.py | 3 ++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 82d1667d..ae0dd356 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -94,7 +94,10 @@ class HistoryEntry(models.Model): def owner(self): pk = self.user["pk"] model = apps.get_model("users", "User") - return model.objects.get(pk=pk) + try: + return model.objects.get(pk=pk) + except model.DoesNotExist: + return None @cached_property def values_diff(self): diff --git a/taiga/projects/history/serializers.py b/taiga/projects/history/serializers.py index 2b5f4e00..ac6cf0d2 100644 --- a/taiga/projects/history/serializers.py +++ b/taiga/projects/history/serializers.py @@ -39,12 +39,13 @@ class HistoryEntrySerializer(serializers.ModelSerializer): def get_user(self, entry): user = {"pk": None, "username": None, "name": None, "photo": None, "is_active": False} user.update(entry.user) - user["photo"] = get_photo_or_gravatar_url(entry.owner) - user["is_active"] = entry.owner.is_active - if entry.owner.is_active or entry.owner.is_system: - user["name"] = entry.owner.get_full_name() - user["username"] = entry.owner.username + if entry.owner: + user["is_active"] = entry.owner.is_active + + if entry.owner.is_active or entry.owner.is_system: + user["name"] = entry.owner.get_full_name() + user["username"] = entry.owner.username return user diff --git a/taiga/users/services.py b/taiga/users/services.py index 0ce318bd..4be2f405 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -34,6 +34,7 @@ from taiga.projects.notifications.choices import NotifyLevel from .gravatar import get_gravatar_url +from django.conf import settings def get_and_validate_user(*, username:str, password:str) -> bool: """ @@ -70,7 +71,7 @@ def get_photo_or_gravatar_url(user): """Get the user's photo/gravatar url.""" if user: return get_photo_url(user.photo) if user.photo else get_gravatar_url(user.email) - return "" + return settings.GRAVATAR_DEFAULT_AVATAR def get_big_photo_url(photo): From c60e177c32ca31cb5cf57d814f2fc0060b5504ec Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Mon, 14 Sep 2015 17:43:21 +0200 Subject: [PATCH 127/190] Show email field for the same user or superuser --- taiga/users/api.py | 2 +- tests/integration/test_users.py | 48 ++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/taiga/users/api.py b/taiga/users/api.py index 508e707f..93f42fb1 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -58,7 +58,7 @@ class UsersViewSet(ModelCrudViewSet): def get_serializer_class(self): if self.action in ["partial_update", "update", "retrieve", "by_username"]: user = self.object - if self.request.user == user: + if self.request.user == user or self.request.user.is_superuser: return self.admin_serializer_class return self.serializer_class diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index b658045f..8cb28a58 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -253,6 +253,45 @@ def test_list_contacts_public_projects(client): assert response_content[0]["id"] == user_2.id +def test_mail_permissions(client): + user_1 = f.UserFactory.create(is_superuser=True) + user_2 = f.UserFactory.create() + + url1 = reverse('users-detail', kwargs={"pk": user_1.pk}) + url2 = reverse('users-detail', kwargs={"pk": user_2.pk}) + + # Anonymous user + response = client.json.get(url1) + assert response.status_code == 200 + assert "email" not in response.data + + response = client.json.get(url2) + assert response.status_code == 200 + assert "email" not in response.data + + # Superuser + client.login(user_1) + + response = client.json.get(url1) + assert response.status_code == 200 + assert "email" in response.data + + response = client.json.get(url2) + assert response.status_code == 200 + assert "email" in response.data + + # Normal user + client.login(user_2) + + response = client.json.get(url1) + assert response.status_code == 200 + assert "email" not in response.data + + response = client.json.get(url2) + assert response.status_code == 200 + assert "email" in response.data + + def test_get_favourites_list(): fav_user = f.UserFactory() viewer_user = f.UserFactory() @@ -404,13 +443,16 @@ def test_get_favourites_list_permissions(): f.VoteFactory(content_type=content_type, object_id=issue.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=issue.id, count=1) - #If the project is private a viewer user without any permission shouldn' see any vote + #If the project is private a viewer user without any permission shouldn' see + # any vote assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 0 - #If the project is private but the viewer user has permissions the votes should be accesible + #If the project is private but the viewer user has permissions the votes should + # be accesible assert len(get_favourites_list(fav_user, viewer_priviliged_user)) == 4 - #If the project is private but has the required anon permissions the votes should be accesible by any user too + #If the project is private but has the required anon permissions the votes should + # be accesible by any user too project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] project.save() assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 4 From f0926bc0e6aa32ea58ec6f2b41b5da1d49a1dc53 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 9 Sep 2015 08:57:11 +0200 Subject: [PATCH 128/190] Fixing mentions for users with capitalized usernames --- taiga/mdrender/extensions/mentions.py | 2 +- tests/integration/test_mdrender.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/taiga/mdrender/extensions/mentions.py b/taiga/mdrender/extensions/mentions.py index 0664bd94..83eeae20 100644 --- a/taiga/mdrender/extensions/mentions.py +++ b/taiga/mdrender/extensions/mentions.py @@ -32,7 +32,7 @@ from taiga.users.models import User class MentionsExtension(Extension): def extendMarkdown(self, md, md_globals): - MENTION_RE = r'(@)([a-z0-9.-\.]+)' + MENTION_RE = r'(@)([a-zA-Z0-9.-\.]+)' mentionsPattern = MentionsPattern(MENTION_RE) mentionsPattern.md = md md.inlinePatterns.add('mentions', mentionsPattern, '_end') diff --git a/tests/integration/test_mdrender.py b/tests/integration/test_mdrender.py index 3735eac2..6710a014 100644 --- a/tests/integration/test_mdrender.py +++ b/tests/integration/test_mdrender.py @@ -47,6 +47,12 @@ def test_render_and_extract_mentions(): (_, extracted) = render_and_extract(dummy_project, "**@user1**") assert extracted['mentions'] == [user] +def test_render_and_extract_mentions_with_capitalized_username(): + user = factories.UserFactory(username="User1", full_name="test") + (_, extracted) = render_and_extract(dummy_project, "**@User1**") + assert extracted['mentions'] == [user] + + def test_proccessor_valid_email(): result = render(dummy_project, "**beta.tester@taiga.io**") expected_result = "

beta.tester@taiga.io

" From fd2037f456b5aac88fad3a148b14a4e0f50d7d65 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 15 Sep 2015 13:53:14 +0200 Subject: [PATCH 129/190] Issue 2991 - When a new comment is added the update of watchers list applies in the next future change --- taiga/projects/history/mixins.py | 4 +++- taiga/projects/notifications/mixins.py | 2 +- taiga/projects/notifications/services.py | 16 ++++++++++++---- tests/integration/test_notifications.py | 6 +++--- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/taiga/projects/history/mixins.py b/taiga/projects/history/mixins.py index 9e379f77..45a7c97e 100644 --- a/taiga/projects/history/mixins.py +++ b/taiga/projects/history/mixins.py @@ -17,7 +17,7 @@ import warnings from .services import take_snapshot - +from taiga.projects.notifications import services as notifications_services class HistoryResourceMixin(object): """ @@ -63,6 +63,8 @@ class HistoryResourceMixin(object): if sobj != obj and delete: delete = False + notifications_services.analize_object_for_watchers(obj, comment, user) + self.__last_history = take_snapshot(sobj, comment=comment, user=user, delete=delete) self.__object_saved = True diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index bf42a9ed..ee41fd55 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -91,7 +91,7 @@ class WatchedResourceMixin: # some text fields for extract mentions and add them # to watchers before obtain a complete list of # notifiable users. - services.analize_object_for_watchers(obj, history) + services.analize_object_for_watchers(obj, history.comment, history.owner) # Get a complete list of notifiable users for current # object and send the change notification to them. diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 5d191d32..1cc99dc6 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -90,16 +90,23 @@ def get_notify_policy(project, user): return instance -def analize_object_for_watchers(obj:object, history:object): +def analize_object_for_watchers(obj:object, comment:str, user:object): """ Generic implementation for analize model objects and extract mentions from it and add it to watchers. """ + + if not hasattr(obj, "get_project"): + return + + if not hasattr(obj, "add_watcher"): + return + from taiga import mdrender as mdr texts = (getattr(obj, "description", ""), getattr(obj, "content", ""), - getattr(history, "comment", ""),) + comment,) _, data = mdr.render_and_extract(obj.get_project(), "\n".join(texts)) @@ -108,8 +115,9 @@ def analize_object_for_watchers(obj:object, history:object): obj.add_watcher(user) # Adding the person who edited the object to the watchers - if history.comment and not history.owner.is_system: - obj.add_watcher(history.owner) + if comment and not user.is_system: + obj.add_watcher(user) + def _filter_by_permissions(obj, user): UserStory = apps.get_model("userstories", "UserStory") diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 4442ad92..d183a9ef 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -104,7 +104,7 @@ def test_analize_object_for_watchers(): history = MagicMock() history.comment = "" - services.analize_object_for_watchers(issue, history) + services.analize_object_for_watchers(issue, history.comment, history.owner) assert issue.add_watcher.call_count == 2 @@ -119,7 +119,7 @@ def test_analize_object_for_watchers_adding_owner_non_empty_comment(): history.comment = "Comment" history.owner = user1 - services.analize_object_for_watchers(issue, history) + services.analize_object_for_watchers(issue, history.comment, history.owner) assert issue.add_watcher.call_count == 1 @@ -134,7 +134,7 @@ def test_analize_object_for_watchers_no_adding_owner_empty_comment(): history.comment = "" history.owner = user1 - services.analize_object_for_watchers(issue, history) + services.analize_object_for_watchers(issue, history.comment, history.owner) assert issue.add_watcher.call_count == 0 From 275b2950ef7b561dd04561cfe139996a4d1ec91f Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 11 Sep 2015 08:15:24 +0200 Subject: [PATCH 130/190] Refactoring search system --- CHANGELOG.md | 2 ++ taiga/base/filters.py | 12 ++++---- taiga/base/utils/db.py | 6 ++++ .../migrations/0026_auto_20150911_1237.py | 29 +++++++++++++++++++ taiga/searches/services.py | 26 ++++++++--------- tests/integration/test_searches.py | 5 ++-- 6 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 taiga/projects/migrations/0026_auto_20150911_1237.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc96593..ed0e5c9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,13 @@ - Add endpoints to show the watchers list for issues, tasks and user stories. - Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). - Add externall apps: now Taiga can integrate with hundreds of applications and service. +- Improving searching system, now full text searchs are supported - i18n. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. - Add russian (ru) translation. + ### Misc - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) diff --git a/taiga/base/filters.py b/taiga/base/filters.py index e0de384c..d662b47d 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -24,7 +24,7 @@ from django.utils.translation import ugettext as _ from taiga.base import exceptions as exc from taiga.base.api.utils import get_object_or_404 - +from taiga.base.utils.db import to_tsquery logger = logging.getLogger(__name__) @@ -487,11 +487,11 @@ class QFilter(FilterBackend): def filter_queryset(self, request, queryset, view): q = request.QUERY_PARAMS.get('q', None) if q: - if q.isdigit(): - qs_args = [Q(ref=q)] - else: - qs_args = [Q(subject__icontains=x) for x in q.split()] + table = queryset.model._meta.db_table + where_clause = ("to_tsvector('english_nostop', coalesce({table}.subject, '') || ' ' || " + "coalesce({table}.ref) || ' ' || " + "coalesce({table}.description, '')) @@ to_tsquery('english_nostop', %s)".format(table=table)) - queryset = queryset.filter(reduce(operator.and_, qs_args)) + queryset = queryset.extra(where=[where_clause], params=[to_tsquery(q)]) return queryset diff --git a/taiga/base/utils/db.py b/taiga/base/utils/db.py index 40afea81..2762833e 100644 --- a/taiga/base/utils/db.py +++ b/taiga/base/utils/db.py @@ -125,3 +125,9 @@ def update_in_bulk_with_ids(ids, list_of_new_values, model): """ for id, new_values in zip(ids, list_of_new_values): model.objects.filter(id=id).update(**new_values) + + +def to_tsquery(text): + # We want to transform a query like "exam proj" (should find "project example") to something like proj:* & exam:* + search_elems = ["{}:*".format(search_elem) for search_elem in text.split(" ")] + return " & ".join(search_elems) diff --git a/taiga/projects/migrations/0026_auto_20150911_1237.py b/taiga/projects/migrations/0026_auto_20150911_1237.py new file mode 100644 index 00000000..073c2349 --- /dev/null +++ b/taiga/projects/migrations/0026_auto_20150911_1237.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import connection +from django.db import migrations + + +def create_postgres_search_dictionary(apps, schema_editor): + sql=""" +CREATE TEXT SEARCH DICTIONARY english_stem_nostop ( + Template = snowball, + Language = english +); +CREATE TEXT SEARCH CONFIGURATION public.english_nostop ( COPY = pg_catalog.english ); +ALTER TEXT SEARCH CONFIGURATION public.english_nostop +ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, hword, hword_part, word WITH english_stem_nostop; +""" + cursor = connection.cursor() + cursor.execute(sql) + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0025_auto_20150901_1600'), + ] + + operations = [ + migrations.RunPython(create_postgres_search_dictionary), + ] diff --git a/taiga/searches/services.py b/taiga/searches/services.py index 495e298d..dcac7f33 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -16,20 +16,20 @@ from django.apps import apps from django.conf import settings - +from taiga.base.utils.db import to_tsquery MAX_RESULTS = getattr(settings, "SEARCHES_MAX_RESULTS", 150) def search_user_stories(project, text): model_cls = apps.get_model("userstories", "UserStory") - where_clause = ("to_tsvector(coalesce(userstories_userstory.subject) || ' ' || " + where_clause = ("to_tsvector('english_nostop', coalesce(userstories_userstory.subject) || ' ' || " "coalesce(userstories_userstory.ref) || ' ' || " "coalesce(userstories_userstory.description, '')) " - "@@ plainto_tsquery(%s)") + "@@ to_tsquery('english_nostop', %s)") if text: - return (model_cls.objects.extra(where=[where_clause], params=[text]) + return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) .filter(project_id=project.pk)[:MAX_RESULTS]) return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] @@ -37,12 +37,12 @@ def search_user_stories(project, text): def search_tasks(project, text): model_cls = apps.get_model("tasks", "Task") - where_clause = ("to_tsvector(coalesce(tasks_task.subject, '') || ' ' || " + where_clause = ("to_tsvector('english_nostop', coalesce(tasks_task.subject, '') || ' ' || " "coalesce(tasks_task.ref) || ' ' || " - "coalesce(tasks_task.description, '')) @@ plainto_tsquery(%s)") + "coalesce(tasks_task.description, '')) @@ to_tsquery('english_nostop', %s)") if text: - return (model_cls.objects.extra(where=[where_clause], params=[text]) + return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) .filter(project_id=project.pk)[:MAX_RESULTS]) return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] @@ -50,12 +50,12 @@ def search_tasks(project, text): def search_issues(project, text): model_cls = apps.get_model("issues", "Issue") - where_clause = ("to_tsvector(coalesce(issues_issue.subject) || ' ' || " + where_clause = ("to_tsvector('english_nostop', coalesce(issues_issue.subject) || ' ' || " "coalesce(issues_issue.ref) || ' ' || " - "coalesce(issues_issue.description, '')) @@ plainto_tsquery(%s)") + "coalesce(issues_issue.description, '')) @@ to_tsquery('english_nostop', %s)") if text: - return (model_cls.objects.extra(where=[where_clause], params=[text]) + return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) .filter(project_id=project.pk)[:MAX_RESULTS]) return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] @@ -63,12 +63,12 @@ def search_issues(project, text): def search_wiki_pages(project, text): model_cls = apps.get_model("wiki", "WikiPage") - where_clause = ("to_tsvector(coalesce(wiki_wikipage.slug) || ' ' || " + where_clause = ("to_tsvector('english_nostop', coalesce(wiki_wikipage.slug) || ' ' || " "coalesce(wiki_wikipage.content, '')) " - "@@ plainto_tsquery(%s)") + "@@ to_tsquery('english_nostop', %s)") if text: - return (model_cls.objects.extra(where=[where_clause], params=[text]) + return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) .filter(project_id=project.pk)[:MAX_RESULTS]) return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index ec6743ad..db026a2a 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -124,10 +124,11 @@ def test_search_text_query_in_my_project(client, searches_initial_data): response = client.get(reverse("search-list"), {"project": data.project1.id, "text": "back"}) assert response.status_code == 200 - assert response.data["count"] == 2 + assert response.data["count"] == 3 assert len(response.data["userstories"]) == 1 assert len(response.data["tasks"]) == 1 - assert len(response.data["issues"]) == 0 + # Back is a backend substring + assert len(response.data["issues"]) == 1 assert len(response.data["wikipages"]) == 0 From 69f5b609fbb828f3223fe1c693c4cfdf8f62bfe1 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 16 Sep 2015 08:46:36 +0200 Subject: [PATCH 131/190] Issue 3147: Remove user.photo file on anonimize --- taiga/users/api.py | 3 +- taiga/users/models.py | 15 +++++++++ tests/integration/test_users.py | 55 +++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/taiga/users/api.py b/taiga/users/api.py index 93f42fb1..e9696bfd 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -233,6 +233,7 @@ class UsersViewSet(ModelCrudViewSet): except Exception: raise exc.WrongArguments(_("Invalid image format")) + request.user.delete_photo() request.user.photo = avatar request.user.save(update_fields=["photo"]) user_data = self.admin_serializer_class(request.user).data @@ -245,7 +246,7 @@ class UsersViewSet(ModelCrudViewSet): Remove the avatar of current logged user. """ self.check_permissions(request, "remove_avatar", None) - request.user.photo = None + request.user.delete_photo() request.user.save(update_fields=["photo"]) user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) diff --git a/taiga/users/models.py b/taiga/users/models.py index 345115be..38b34559 100644 --- a/taiga/users/models.py +++ b/taiga/users/models.py @@ -40,6 +40,8 @@ from taiga.base.utils.slug import slugify_uniquely from taiga.base.utils.iterators import split_by_n from taiga.permissions.permissions import MEMBERS_PERMISSIONS +from easy_thumbnails.files import get_thumbnailer + def generate_random_hex_color(): return "#{:06x}".format(random.randint(0,0xFFFFFF)) @@ -174,9 +176,22 @@ class User(AbstractBaseUser, PermissionsMixin): self.colorize_tags = True self.token = None self.set_unusable_password() + self.delete_photo() self.save() self.auth_data.all().delete() + def delete_photo(self): + # Removing thumbnails + thumbnailer = get_thumbnailer(self.photo) + thumbnailer.delete_thumbnails() + + # Removing original photo + if self.photo: + storage, path = self.photo.storage, self.photo.path + storage.delete(path) + + self.photo = None + class Role(models.Model): name = models.CharField(max_length=200, null=False, blank=False, diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 8cb28a58..f20049f1 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -3,6 +3,7 @@ from tempfile import NamedTemporaryFile from django.contrib.contenttypes.models import ContentType from django.core.urlresolvers import reverse +from django.core.files import File from .. import factories as f @@ -12,6 +13,10 @@ from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS from taiga.users.services import get_favourites_list +from easy_thumbnails.files import generate_all_aliases, get_thumbnailer + +import os + pytestmark = pytest.mark.django_db @@ -193,6 +198,56 @@ def test_change_avatar(client): assert response.status_code == 200 +def test_change_avatar_removes_the_old_one(client): + url = reverse('users-change-avatar') + user = f.UserFactory() + + with NamedTemporaryFile(delete=False) as avatar: + avatar.write(DUMMY_BMP_DATA) + avatar.seek(0) + user.photo = File(avatar) + user.save() + generate_all_aliases(user.photo, include_global=True) + + with NamedTemporaryFile(delete=False) as avatar: + thumbnailer = get_thumbnailer(user.photo) + original_photo_paths = [user.photo.path] + original_photo_paths += [th.path for th in thumbnailer.get_thumbnails()] + assert list(map(os.path.exists, original_photo_paths)) == [True, True, True, True] + + client.login(user) + avatar.write(DUMMY_BMP_DATA) + avatar.seek(0) + post_data = {'avatar': avatar} + response = client.post(url, post_data) + + assert response.status_code == 200 + assert list(map(os.path.exists, original_photo_paths)) == [False, False, False, False] + + +def test_remove_avatar(client): + url = reverse('users-remove-avatar') + user = f.UserFactory() + + with NamedTemporaryFile(delete=False) as avatar: + avatar.write(DUMMY_BMP_DATA) + avatar.seek(0) + user.photo = File(avatar) + user.save() + generate_all_aliases(user.photo, include_global=True) + + thumbnailer = get_thumbnailer(user.photo) + original_photo_paths = [user.photo.path] + original_photo_paths += [th.path for th in thumbnailer.get_thumbnails()] + assert list(map(os.path.exists, original_photo_paths)) == [True, True, True, True] + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + assert list(map(os.path.exists, original_photo_paths)) == [False, False, False, False] + + def test_list_contacts_private_projects(client): project = f.ProjectFactory.create() user_1 = f.UserFactory.create() From 0d21f04a876f29304e7587a351ddd9916120e2ef Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 16 Sep 2015 12:23:01 +0200 Subject: [PATCH 132/190] Adding votes_count sorting to issues and userstories API --- taiga/projects/issues/api.py | 3 ++- taiga/projects/userstories/api.py | 3 ++- taiga/projects/votes/utils.py | 11 +++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index 5035418b..e1259e10 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -75,7 +75,8 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W "modified_date", "owner", "assigned_to", - "subject") + "subject", + "votes_count") def get_serializer_class(self, *args, **kwargs): if self.action in ["retrieve", "by_ref"]: diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 9869159a..4c4ebf59 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -69,7 +69,8 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi "status__is_closed"] order_by_fields = ["backlog_order", "sprint_order", - "kanban_order"] + "kanban_order", + "votes_count"] # Specific filter used for filtering neighbor user stories _neighbor_tags_filter = filters.TagsFilter('neighbor_tags') diff --git a/taiga/projects/votes/utils.py b/taiga/projects/votes/utils.py index bff72a6a..f82b17b0 100644 --- a/taiga/projects/votes/utils.py +++ b/taiga/projects/votes/utils.py @@ -34,10 +34,13 @@ def attach_votes_count_to_queryset(queryset, as_field="votes_count"): """ model = queryset.model type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) - sql = ("""SELECT coalesce(votes_votes.count, 0) - FROM votes_votes - WHERE votes_votes.content_type_id = {type_id} - AND votes_votes.object_id = {tbl}.id""") + sql = """SELECT coalesce(SUM(votes_count), 0) FROM ( + SELECT coalesce(votes_votes.count, 0) votes_count + FROM votes_votes + WHERE votes_votes.content_type_id = {type_id} + AND votes_votes.object_id = {tbl}.id + ) as e""" + sql = sql.format(type_id=type.id, tbl=model._meta.db_table) qs = queryset.extra(select={as_field: sql}) return qs From bf57ace9a2a5229ace08ce034f6ab618eb85273c Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 9 Sep 2015 15:32:18 +0200 Subject: [PATCH 133/190] Fix some errors related to watched and likes lists --- taiga/projects/notifications/mixins.py | 37 +++++++------- taiga/projects/notifications/services.py | 17 +++++++ taiga/users/serializers.py | 62 ++++++++++++++++++---- taiga/users/services.py | 45 +++++++++++----- tests/integration/test_users.py | 65 ++++++++++++++++-------- tests/integration/test_userstories.py | 16 ++++++ 6 files changed, 181 insertions(+), 61 deletions(-) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index ee41fd55..27a93d88 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -189,29 +189,30 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): #watchers is not a field from the model but can be attached in the get_queryset of the viewset. #If that's the case we need to remove it before calling the super method watcher_field = self.fields.pop("watchers", None) - instance = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) - if instance is not None and self.validate_watchers(attrs, "watchers"): - #A partial update can exclude the watchers field - if not "watchers" in attrs: - return instance + self.validate_watchers(attrs, "watchers") + new_watcher_ids = set(attrs.pop("watchers", [])) + obj = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) - new_watcher_ids = set(attrs.get("watchers", None)) - old_watcher_ids = set(instance.get_watchers().values_list("id", flat=True)) - adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) - removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) + #A partial update can exclude the watchers field or if the new instance can still not be saved + if instance is None or len(new_watcher_ids) == 0: + return obj - User = apps.get_model("users", "User") - adding_users = User.objects.filter(id__in=adding_watcher_ids) - removing_users = User.objects.filter(id__in=removing_watcher_ids) - for user in adding_users: - services.add_watcher(instance, user) + old_watcher_ids = set(obj.get_watchers().values_list("id", flat=True)) + adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) + removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) - for user in removing_users: - services.remove_watcher(instance, user) + User = apps.get_model("users", "User") + adding_users = User.objects.filter(id__in=adding_watcher_ids) + removing_users = User.objects.filter(id__in=removing_watcher_ids) + for user in adding_users: + services.add_watcher(obj, user) - instance.watchers = instance.get_watchers() + for user in removing_users: + services.remove_watcher(obj, user) - return instance + obj.watchers = obj.get_watchers() + + return obj def to_native(self, obj): #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 1cc99dc6..e4bd8a3f 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -367,6 +367,23 @@ def get_watched(user_or_id, model): params=(obj_type.id, user_id)) +def get_projects_watched(user_or_id): + """Get the objects watched by an user. + + :param user_or_id: :class:`~taiga.users.models.User` instance or id. + :param model: Show only objects of this kind. Can be any Django model class. + + :return: Queryset of objects representing the votes of the user. + """ + + if isinstance(user_or_id, get_user_model()): + user_id = user_or_id.id + else: + user_id = user_or_id + + project_class = apps.get_model("projects", "Project") + return project_class.objects.filter(notify_policies__user__id=user_id).exclude(notify_policies__notify_level=NotifyLevel.ignore) + def add_watcher(obj, user): """Add a watcher to an object. diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 3158f5f8..2a9abade 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -165,27 +165,31 @@ class FavouriteSerializer(serializers.Serializer): id = serializers.IntegerField() ref = serializers.IntegerField() slug = serializers.CharField() + name = serializers.CharField() subject = serializers.CharField() - tags = TagsField(default=[]) - project = serializers.IntegerField() + description = serializers.SerializerMethodField("get_description") assigned_to = serializers.IntegerField() - total_watchers = serializers.IntegerField() + status = serializers.CharField() + status_color = serializers.CharField() + tags_colors = serializers.SerializerMethodField("get_tags_color") + created_date = serializers.DateTimeField() + is_private = serializers.SerializerMethodField("get_is_private") is_voted = serializers.SerializerMethodField("get_is_voted") is_watched = serializers.SerializerMethodField("get_is_watched") - created_date = serializers.DateTimeField() + total_watchers = serializers.IntegerField() + total_votes = serializers.IntegerField() - project_name = serializers.CharField() - project_slug = serializers.CharField() - project_is_private = serializers.CharField() + project = serializers.SerializerMethodField("get_project") + project_name = serializers.SerializerMethodField("get_project_name") + project_slug = serializers.SerializerMethodField("get_project_slug") + project_is_private = serializers.SerializerMethodField("get_project_is_private") assigned_to_username = serializers.CharField() assigned_to_full_name = serializers.CharField() assigned_to_photo = serializers.SerializerMethodField("get_photo") - total_votes = serializers.IntegerField() - def __init__(self, *args, **kwargs): # Don't pass the extra ids args up to the superclass self.user_votes = kwargs.pop("user_votes", {}) @@ -194,6 +198,38 @@ class FavouriteSerializer(serializers.Serializer): # Instantiate the superclass normally super(FavouriteSerializer, self).__init__(*args, **kwargs) + def _none_if_project(self, obj, property): + type = obj.get("type", "") + if type == "project": + return None + + return obj.get(property) + + def _none_if_not_project(self, obj, property): + type = obj.get("type", "") + if type != "project": + return None + + return obj.get(property) + + def get_project(self, obj): + return self._none_if_project(obj, "project") + + def get_is_private(self, obj): + return self._none_if_not_project(obj, "project_is_private") + + def get_project_name(self, obj): + return self._none_if_project(obj, "project_name") + + def get_description(self, obj): + return self._none_if_not_project(obj, "description") + + def get_project_slug(self, obj): + return self._none_if_project(obj, "project_slug") + + def get_project_is_private(self, obj): + return self._none_if_project(obj, "project_is_private") + def get_is_voted(self, obj): return obj["id"] in self.user_votes.get(obj["type"], []) @@ -201,6 +237,14 @@ class FavouriteSerializer(serializers.Serializer): return obj["id"] in self.user_watching.get(obj["type"], []) def get_photo(self, obj): + type = obj.get("type", "") + if type == "project": + return None + UserData = namedtuple("UserData", ["photo", "email"]) user_data = UserData(photo=obj["assigned_to_photo"], email=obj.get("assigned_to_email") or "") return get_photo_or_gravatar_url(user_data) + + def get_tags_color(self, obj): + tags = obj.get("tags", []) + return [{"name": tc[0], "color": tc[1]} for tc in obj.get("tags_colors", []) if tc[0] in tags] diff --git a/taiga/users/services.py b/taiga/users/services.py index 4be2f405..580d615b 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -31,7 +31,7 @@ from easy_thumbnails.exceptions import InvalidImageFormatError from taiga.base import exceptions as exc from taiga.base.utils.urls import get_absolute_url from taiga.projects.notifications.choices import NotifyLevel - +from taiga.projects.notifications.services import get_projects_watched from .gravatar import get_gravatar_url from django.conf import settings @@ -179,6 +179,11 @@ def get_watched_content_for_user(user): list.append(object_id) user_watches[ct_model] = list + #Now for projects, + projects_watched = get_projects_watched(user) + project_content_type_model=ContentType.objects.get(app_label="projects", model="project").model + user_watches[project_content_type_model] = projects_watched.values_list("id", flat=True) + return user_watches @@ -186,8 +191,9 @@ def _build_favourites_sql_for_projects(for_user): sql = """ SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'watch' AS action, tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project, - slug AS slug, projects_project.name AS subject, - notifications_notifypolicy.created_at, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to + slug AS slug, projects_project.name AS name, null AS subject, + notifications_notifypolicy.created_at as created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to, + null as status, null as status_color FROM notifications_notifypolicy INNER JOIN projects_project ON (projects_project.id = notifications_notifypolicy.project_id) @@ -203,8 +209,9 @@ def _build_favourites_sql_for_projects(for_user): UNION SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'vote' AS action, tags, votes_vote.object_id AS object_id, projects_project.id AS project, - slug AS slug, projects_project.name AS subject, - votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to + slug AS slug, projects_project.name AS name, null AS subject, + votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to, + null as status, null as status_color FROM votes_vote INNER JOIN projects_project ON (projects_project.id = votes_vote.object_id) @@ -216,7 +223,7 @@ def _build_favourites_sql_for_projects(for_user): ON projects_project.id = type_watchers.project_id LEFT JOIN votes_votes ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) - WHERE votes_vote.user_id = {for_user_id} + WHERE votes_vote.user_id = {for_user_id} AND {project_content_type_id} = votes_vote.content_type_id """ sql = sql.format( for_user_id=for_user.id, @@ -232,13 +239,16 @@ def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", sql = """ SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'watch' AS action, tags, notifications_watched.object_id AS object_id, {table_name}.{project_column} AS project, - {slug_column} AS slug, {subject_column} AS subject, - notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to + {slug_column} AS slug, null AS name, {subject_column} AS subject, + notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to, + projects_{type}status.name as status, projects_{type}status.color as status_color FROM notifications_watched INNER JOIN django_content_type ON (notifications_watched.content_type_id = django_content_type.id AND django_content_type.model = '{type}') INNER JOIN {table_name} ON ({table_name}.id = notifications_watched.object_id) + INNER JOIN projects_{type}status + ON (projects_{type}status.id = {table_name}.status_id) LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id LEFT JOIN votes_votes @@ -247,13 +257,16 @@ def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", UNION SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'vote' AS action, tags, votes_vote.object_id AS object_id, {table_name}.{project_column} AS project, - {slug_column} AS slug, {subject_column} AS subject, - votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to + {slug_column} AS slug, null AS name, {subject_column} AS subject, + votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to, + projects_{type}status.name as status, projects_{type}status.color as status_color FROM votes_vote INNER JOIN django_content_type ON (votes_vote.content_type_id = django_content_type.id AND django_content_type.model = '{type}') INNER JOIN {table_name} ON ({table_name}.id = votes_vote.object_id) + INNER JOIN projects_{type}status + ON (projects_{type}status.id = {table_name}.status_id) LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id LEFT JOIN votes_votes @@ -279,12 +292,18 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): filters_sql += " AND action = '{action}' ".format(action=action) if q: - filters_sql += " AND to_tsvector(coalesce(subject, '')) @@ plainto_tsquery('{q}') ".format(q=q) + # We must transform a q like "proj exam" (should find "project example") to something like proj:* & exam:* + qs = ["{}:*".format(e) for e in q.split(" ")] + filters_sql += """ AND ( + to_tsvector(coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('{q}') + ) + """.format(q=" & ".join(qs)) sql = """ -- BEGIN Basic info: we need to mix info from different tables and denormalize it SELECT entities.*, - projects_project.name as project_name, projects_project.slug as project_slug, projects_project.is_private as project_is_private, + projects_project.name as project_name, projects_project.description as description, projects_project.slug as project_slug, projects_project.is_private as project_is_private, + projects_project.tags_colors, users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email FROM ( {userstories_sql} @@ -332,7 +351,7 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): -- END Permissions checking {filters_sql} - ORDER BY entities.created_date; + ORDER BY entities.created_date DESC; """ from_user_id = -1 diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index f20049f1..4ad11470 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -9,6 +9,7 @@ from .. import factories as f from taiga.base.utils import json from taiga.users import models +from taiga.users.serializers import FavouriteSerializer from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS from taiga.users.services import get_favourites_list @@ -396,32 +397,43 @@ def test_get_favourites_list_valid_info_for_project(): viewer_user = f.UserFactory() watcher_user = f.UserFactory() - project = f.ProjectFactory(is_private=False, name="Testing project") + project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) project.add_watcher(watcher_user) content_type = ContentType.objects.get_for_model(project) vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=project.id, count=1) - project_vote_info = get_favourites_list(fav_user, viewer_user)[0] + raw_project_vote_info = get_favourites_list(fav_user, viewer_user)[0] + project_vote_info = FavouriteSerializer(raw_project_vote_info).data + assert project_vote_info["type"] == "project" assert project_vote_info["action"] == "vote" assert project_vote_info["id"] == project.id assert project_vote_info["ref"] == None assert project_vote_info["slug"] == project.slug - assert project_vote_info["subject"] == project.name - assert project_vote_info["tags"] == project.tags - assert project_vote_info["project"] == project.id + assert project_vote_info["name"] == project.name + assert project_vote_info["subject"] == None + assert project_vote_info["description"] == project.description assert project_vote_info["assigned_to"] == None + assert project_vote_info["status"] == None + assert project_vote_info["status_color"] == None + + tags_colors = {tc["name"]:tc["color"] for tc in project_vote_info["tags_colors"]} + assert "test" in tags_colors + assert "tag" in tags_colors + + assert project_vote_info["is_private"] == project.is_private + assert project_vote_info["is_voted"] == False + assert project_vote_info["is_watched"] == False assert project_vote_info["total_watchers"] == 1 - assert project_vote_info["created_date"] == vote.created_date - assert project_vote_info["project_name"] == project.name - assert project_vote_info["project_slug"] == project.slug - assert project_vote_info["project_is_private"] == project.is_private + assert project_vote_info["total_votes"] == 1 + assert project_vote_info["project"] == None + assert project_vote_info["project_name"] == None + assert project_vote_info["project_slug"] == None + assert project_vote_info["project_is_private"] == None assert project_vote_info["assigned_to_username"] == None assert project_vote_info["assigned_to_full_name"] == None assert project_vote_info["assigned_to_photo"] == None - assert project_vote_info["assigned_to_email"] == None - assert project_vote_info["total_votes"] == 1 def test_get_favourites_list_valid_info_for_not_project_types(): @@ -449,26 +461,37 @@ def test_get_favourites_list_valid_info_for_not_project_types(): vote = f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) - instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0] + raw_instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0] + instance_vote_info = FavouriteSerializer(raw_instance_vote_info).data + assert instance_vote_info["type"] == object_type assert instance_vote_info["action"] == "vote" assert instance_vote_info["id"] == instance.id assert instance_vote_info["ref"] == instance.ref assert instance_vote_info["slug"] == None + assert instance_vote_info["name"] == None assert instance_vote_info["subject"] == instance.subject - assert instance_vote_info["tags"] == instance.tags - assert instance_vote_info["project"] == instance.project.id - assert instance_vote_info["assigned_to"] == assigned_to_user.id + assert instance_vote_info["description"] == None + assert instance_vote_info["assigned_to"] == instance.assigned_to.id + assert instance_vote_info["status"] == instance.status.name + assert instance_vote_info["status_color"] == instance.status.color + + tags_colors = {tc["name"]:tc["color"] for tc in instance_vote_info["tags_colors"]} + assert "test1" in tags_colors + assert "test2" in tags_colors + + assert instance_vote_info["is_private"] == None + assert instance_vote_info["is_voted"] == False + assert instance_vote_info["is_watched"] == False assert instance_vote_info["total_watchers"] == 1 - assert instance_vote_info["created_date"] == vote.created_date + assert instance_vote_info["total_votes"] == 3 + assert instance_vote_info["project"] == instance.project.id assert instance_vote_info["project_name"] == instance.project.name assert instance_vote_info["project_slug"] == instance.project.slug assert instance_vote_info["project_is_private"] == instance.project.is_private - assert instance_vote_info["assigned_to_username"] == assigned_to_user.username - assert instance_vote_info["assigned_to_full_name"] == assigned_to_user.full_name - assert instance_vote_info["assigned_to_photo"] == '' - assert instance_vote_info["assigned_to_email"] == assigned_to_user.email - assert instance_vote_info["total_votes"] == 3 + assert instance_vote_info["assigned_to_username"] == instance.assigned_to.username + assert instance_vote_info["assigned_to_full_name"] == instance.assigned_to.full_name + assert instance_vote_info["assigned_to_photo"] != "" def test_get_favourites_list_permissions(): diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index f90fd97a..787656e8 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -46,6 +46,22 @@ def test_update_userstories_order_in_bulk(): model=models.UserStory) +def test_create_userstory_with_watchers(client): + user = f.UserFactory.create() + user_watcher = f.UserFactory.create() + project = f.ProjectFactory.create(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.MembershipFactory.create(project=project, user=user_watcher, is_owner=True) + url = reverse("userstories-list") + + data = {"subject": "Test user story", "project": project.id, "watchers": [user_watcher.id]} + client.login(user) + response = client.json.post(url, json.dumps(data)) + print(response.data) + assert response.status_code == 201 + assert response.data["watchers"] == [] + + def test_create_userstory_without_status(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) From 6e55ccdafb3580ee593c09da638a462e6f0274cd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 17 Sep 2015 12:05:11 +0200 Subject: [PATCH 134/190] Using last searching features in favourites calculation --- taiga/users/services.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/taiga/users/services.py b/taiga/users/services.py index 580d615b..e8df5362 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -29,6 +29,7 @@ from easy_thumbnails.files import get_thumbnailer from easy_thumbnails.exceptions import InvalidImageFormatError from taiga.base import exceptions as exc +from taiga.base.utils.db import to_tsquery from taiga.base.utils.urls import get_absolute_url from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.services import get_projects_watched @@ -292,12 +293,10 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): filters_sql += " AND action = '{action}' ".format(action=action) if q: - # We must transform a q like "proj exam" (should find "project example") to something like proj:* & exam:* - qs = ["{}:*".format(e) for e in q.split(" ")] filters_sql += """ AND ( - to_tsvector(coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('{q}') + to_tsvector('english_nostop', coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('english_nostop', '{q}') ) - """.format(q=" & ".join(qs)) + """.format(q=to_tsquery(q)) sql = """ -- BEGIN Basic info: we need to mix info from different tables and denormalize it From 705cee27249b3ca70c8874b27677a4c321ae4b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 18 Sep 2015 10:53:57 +0200 Subject: [PATCH 135/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 410 ++++++++++---------- taiga/locale/de/LC_MESSAGES/django.po | 410 ++++++++++---------- taiga/locale/en/LC_MESSAGES/django.po | 284 +++++++------- taiga/locale/es/LC_MESSAGES/django.po | 414 ++++++++++---------- taiga/locale/fi/LC_MESSAGES/django.po | 410 ++++++++++---------- taiga/locale/fr/LC_MESSAGES/django.po | 411 ++++++++++---------- taiga/locale/nl/LC_MESSAGES/django.po | 410 ++++++++++---------- taiga/locale/pl/LC_MESSAGES/django.po | 412 ++++++++++---------- taiga/locale/pt_BR/LC_MESSAGES/django.po | 415 ++++++++++---------- taiga/locale/ru/LC_MESSAGES/django.po | 424 +++++++++++---------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 410 ++++++++++---------- 11 files changed, 2301 insertions(+), 2109 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 1d7c7581..4adbae8a 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" @@ -66,6 +66,7 @@ msgid "Error on creating new user." msgstr "Error creant un nou usuari." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Token invàlid" @@ -536,9 +537,9 @@ msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "" @@ -697,11 +698,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] El teu bolcat de dades ha sigut importat" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "Nom" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "Descripció" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "Nom complet" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "Adreça d'email" @@ -712,7 +763,7 @@ msgstr "Comentari" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -784,8 +835,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no és un arxiu json vàlid" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "El projecte no existeix" @@ -926,12 +977,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Veure projecte" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Veure fita" @@ -940,22 +991,22 @@ msgid "View user stories" msgstr "Veure història d'usuari" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Veure tasca" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Veure incidència" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Veure pàgina del wiki" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Veure links del wiki" @@ -983,135 +1034,119 @@ msgstr "Afegeix incidéncies" msgid "Add comments to issues" msgstr "Afegeix comentaris a incidéncies" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Afegeix pàgina del wiki" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Modifica pàgina del wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Afegeix enllaç de wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Modifica enllaç de wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Afegeix fita" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Modifica fita" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Borra fita" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Veure història d'usuari" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Afegeix història d'usuari" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Modifica història d'usuari" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Borra història d'usuari" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Afegeix tasca" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Modifica tasca" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Borra tasca" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Afegeix incidència" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Modifica incidència" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Borra incidència" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Borra pàgina de wiki" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Borra enllaç de wiki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Modifica projecte" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Afegeix membre" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Borra membre" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Borra projecte" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Administrar valors de projecte" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Administrar rols" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Al menys un del usuaris ha de ser administrador" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." @@ -1124,7 +1159,7 @@ msgid "Project ID not matches between object and project" msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1134,14 +1169,14 @@ msgstr "Amo" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "Projecte" @@ -1156,7 +1191,7 @@ msgstr "Id d'objecte" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1170,22 +1205,13 @@ msgstr "Arxiu adjunt" msgid "is deprecated" msgstr "està obsolet " -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "Descripció" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "Ordre" @@ -1213,16 +1239,6 @@ msgstr "" msgid "Multi-Line Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "Nom" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1382,23 +1398,23 @@ msgstr "nota de bloqueig" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tens permissos per a ficar aquest sprint a aquesta incidència" -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "No tens permissos per a ficar aquest status a aquesta tasca" -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "No tens permissos per a ficar aquesta severitat a aquesta tasca" -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "No tens permissos per a ficar aquesta prioritat a aquesta incidència" -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "No tens permissos per a ficar aquest tipus a aquesta incidència" @@ -1444,10 +1460,10 @@ msgstr "assignada a" msgid "external reference" msgstr "referència externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1459,8 +1475,8 @@ msgstr "Data estimada d'inici" msgid "estimated finish date" msgstr "Data estimada de finalització" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "està tancat" @@ -1489,175 +1505,175 @@ msgstr "" msgid "'project' parameter is mandatory" msgstr "" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "email" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "text extra d'invitació" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "L'usuari ja es membre del projecte" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "Points per defecte" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "estatus d'història d'usuai per defecte" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "Estatus de tasca per defecte" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "Prioritat per defecte" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "Severitat per defecte" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "Status d'incidència per defecte" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "Tipus d'incidència per defecte" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "membres" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "total de fites" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "total de punts d'història" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "activa panell de backlog" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "activa panell de kanban" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "activa panell de wiki" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "activa panell d'incidències" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "sistema de videoconferència" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "template de creació" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "permisos d'anònims" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "permisos d'usuaris" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "es privat" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "colors de tags" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "configuració de mòdules" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "està arxivat" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "color" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "limit de treball en progrés" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "rol d'amo per defecte" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "opcions per defecte" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "status d'històries d'usuari" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "punts" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "status de tasques" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "status d'incidències" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "tipus d'incidències" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "prioritats" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "severitats" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "rols" @@ -1689,20 +1705,20 @@ msgstr "" msgid "notify users" msgstr "" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2204,51 +2220,51 @@ msgstr "Versió" msgid "You can't leave the project if there are no more owners" msgstr "No pots deixar el projecte si no hi ha més amos" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Aquest e-mail ja està en ús" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Rol invàlid per al projecte" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Opcions per defecte" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Estatus d'històries d'usuari" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Punts" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Estatus de tasques" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Estatus d'incidéncies" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Tipus d'incidéncies" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Prioritats" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Severitats" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Rols" @@ -2646,15 +2662,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2737,7 +2753,7 @@ msgstr "últim a modificar" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -2753,39 +2769,39 @@ msgstr "Permissos" msgid "Important dates" msgstr "Dates importants" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Nom d'usuari o email invàlid" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "Correu enviat satisfactòriament" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Token invàlid" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Paràmetre de password actual requerit" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Paràmetre de password requerit" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Password invàlid, al menys 6 caràcters requerits" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Password actual invàlid" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Arguments incomplets." -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Format d'image invàlid" @@ -2807,11 +2823,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Invàlid. Estás segur que el token es correcte?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "estatus de superusuari" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -2819,24 +2835,24 @@ msgstr "" "Designa que aquest usuari te tots els permisos sense asignarli-los " "explícitament." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "mot d'usuari" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Requerit. 30 caràcters o menys. Lletres, nombres i caràcters /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Introdueix un nom d'usuari vàlid" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "actiu" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -2844,43 +2860,43 @@ msgstr "" "Designa si aquest usuari ha de se tractac com actiu. Deselecciona açó en " "lloc de borrar el compte." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografia" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "foto" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "data d'unió" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "llenguatge per defecte" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "zona horaria per defecte" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "coloritza tags" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "token de correu" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "nova adreça de correu" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "permissos" @@ -2892,7 +2908,7 @@ msgstr "invàlid" msgid "Invalid username. Try with a different one." msgstr "Nom d'usuari invàlid" -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 748f3192..2202f8ed 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" @@ -74,6 +74,7 @@ msgid "Error on creating new user." msgstr "Fehler bei der Erstellung des neuen Benutzers." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Ungültiges Token" @@ -589,9 +590,9 @@ msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" @@ -843,11 +844,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Ihre Projekt Export-Datei wurde importiert" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "Name" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "Beschreibung" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "vollständiger Name" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "E-Mail Adresse" @@ -858,7 +909,7 @@ msgstr "Kommentar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -930,8 +981,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Die Nutzlast ist kein gültiges json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Das Projekt existiert nicht" @@ -1096,12 +1147,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Projekt ansehen" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Meilensteine ansehen" @@ -1110,22 +1161,22 @@ msgid "View user stories" msgstr "User-Stories ansehen. " #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Aufgaben ansehen" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Tickets ansehen" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Wiki Seiten ansehen" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Wiki Links ansehen" @@ -1153,135 +1204,119 @@ msgstr "Tickets hinzufügen" msgid "Add comments to issues" msgstr "Kommentare zu Tickets hinzufügen" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Wiki Seite hinzufügen" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Wiki Seite ändern" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Wiki Link hinzufügen" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Wiki Link ändern" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Meilenstein hinzufügen" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Meilenstein ändern" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Meilenstein löschen" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "User-Story ansehen" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "User-Story hinzufügen" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "User-Story ändern" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "User-Story löschen" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Aufgabe hinzufügen" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Aufgabe ändern" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Aufgabe löschen" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Ticket hinzufügen" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Ticket ändern" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Gelöschtes Ticket" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Wiki Seite löschen" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Wiki Link löschen" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Projekt ändern" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Mitglied hinzufügen" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Mitglied entfernen" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Projekt löschen" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Administrator Projekt Werte" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Administrator-Rollen" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Unglültiger Templatename" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Ungültige Templatebeschreibung" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Mindestens ein Benutzer muss ein aktiver Administrator sein. " -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" @@ -1294,7 +1329,7 @@ msgid "Project ID not matches between object and project" msgstr "Nr. unterschreidet sich zwischen dem Objekt und dem Projekt" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1304,14 +1339,14 @@ msgstr "Besitzer" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "Projekt" @@ -1326,7 +1361,7 @@ msgstr "Objekt Nr." #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1340,22 +1375,13 @@ msgstr "Angehangene Datei" msgid "is deprecated" msgstr "wurde verworfen" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "Beschreibung" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "Reihenfolge" @@ -1383,16 +1409,6 @@ msgstr "Text" msgid "Multi-Line Text" msgstr "Mehrzeiliger Text" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "Name" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1552,27 +1568,27 @@ msgstr "Blockierungsgrund" msgid "sprint" msgstr "Sprint" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Sprint zu setzen." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diesen Status zu setzen. " -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Gewichtung zu setzen." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Sie haben nicht die Berechtigung, das Ticket auf diese Priorität zu setzen. " -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Sie haben nicht die Berechtigung, das Ticket auf diese Art zu setzen." @@ -1618,10 +1634,10 @@ msgstr "zugewiesen an" msgid "external reference" msgstr "externe Referenz" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "Slug" @@ -1633,8 +1649,8 @@ msgstr "geschätzter Starttermin" msgid "estimated finish date" msgstr "geschätzter Endtermin" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "ist geschlossen" @@ -1663,175 +1679,175 @@ msgstr "'{param}' Parameter ist ein Pflichtfeld" msgid "'project' parameter is mandatory" msgstr "Der 'project' Parameter ist ein Pflichtfeld" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "E-Mail" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "erstellt am " -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "Token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "Einladung Zusatztext " -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "Benutzerreihenfolge" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "Der Benutzer ist bereits Mitglied dieses Projekts" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "voreingestellte Punkte" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "voreingesteller User-Story Status " -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "voreingestellter Aufgabenstatus" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "voreingestellte Priorität " -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "voreingestellte Gewichtung " -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "voreingestellter Ticket Status" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "voreingestellter Ticket Typ" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "Mitglieder" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "Meilensteine Gesamt" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "Story Punkte insgesamt" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "aktives Backlog Panel" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "aktives Kanban Panel" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "aktives Wiki Panel" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "aktives Tickets Panel" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "Videokonferenzsystem" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "Rechte für anonyme Nutzer" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "Rechte für registrierte Nutzer" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "ist privat" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "Tag Farben" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "Module konfigurieren" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "ist archiviert" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "Farbe" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "Ausführungslimit" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "Wert" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "voreingestellte Besitzerrolle" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "Vorgabe Optionen" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "User-Story Status " -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "Punkte" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "Aufgaben Status" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "Ticket Status" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "Ticket Arten" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "Prioritäten" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "Gewichtung" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "Rollen" @@ -1863,20 +1879,20 @@ msgstr "Chronik Einträge" msgid "notify users" msgstr "Benutzer benachrichtigen" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2658,51 +2674,51 @@ msgstr "" "Sie können das Projekt nicht verlassen, wenn keine weiteren Besitzer " "vorhanden sind" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Die E-Mailadresse ist bereits vergeben" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Ungültige Rolle für dieses Projekt" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Die Gesamtzahl der Meilensteine muss größer oder gleich Null sein " -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Voreingestellte Optionen" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Status für User-Stories" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Punkte" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Aufgaben Status" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Ticket Status" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Ticket Arten" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Prioritäten" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Gewichtung" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Rollen" @@ -3121,19 +3137,19 @@ msgstr "Projekteigentümer " msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Sie haben nicht die Berechtigung, diesen Sprint auf diese User-Story zu " "setzen." -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" "Sie haben nicht die Berechtigung, diesen Status auf diese User-Story zu " "setzen." -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3216,7 +3232,7 @@ msgstr "letzte Änderung" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -3232,39 +3248,39 @@ msgstr "Berechtigungen" msgid "Important dates" msgstr "Wichtige Termine" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Ungültiger Benutzername oder E-Mail" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "E-Mail erfolgreich gesendet." -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Token ist ungültig" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Aktueller Passwort Parameter wird benötigt" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Neuer Passwort Parameter benötigt" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Ungültige Passwortlänge, mindestens 6 Zeichen erforderlich" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Ungültiges aktuelles Passwort" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Unvollständige Argumente" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Ungültiges Bildformat" @@ -3287,11 +3303,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Ungültig. Sind Sie sicher, dass das Token korrekt ist?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "Superuser Status" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -3299,25 +3315,25 @@ msgstr "" "Dieser Benutzer soll alle Berechtigungen erhalten, ohne dass diese zuvor " "zugewiesen werden müssen. " -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "Benutzername" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "" "Benötigt. 30 Zeichen oder weniger.. Buchstaben, Zahlen und /./-/_ Zeichen" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Geben Sie einen gültigen Benuzternamen ein." -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "aktiv" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -3325,43 +3341,43 @@ msgstr "" "Kennzeichnet den Benutzer als aktiv. Deaktiviere die Option anstelle einen " "Benutzer zu löschen." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "Über mich" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "Foto" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "Beitrittsdatum" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "Vorgegebene Sprache" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "Standard-Theme" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "Vorgegebene Zeitzone" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "Tag-Farben" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "E-Mail Token" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "neue E-Mail Adresse" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "Berechtigungen" @@ -3373,7 +3389,7 @@ msgstr "ungültig" msgid "Invalid username. Try with a different one." msgstr "Ungültiger Benutzername. Versuchen Sie es mit einem anderen." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Benutzername oder Passwort stimmen mit keinem Benutzer überein." diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 8ba27299..b6a99bcf 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-07 17:00+0200\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -62,6 +62,7 @@ msgid "Error on creating new user." msgstr "" #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "" @@ -686,11 +687,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "" @@ -701,7 +752,7 @@ msgstr "" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -757,8 +808,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "" @@ -1056,19 +1107,19 @@ msgstr "" msgid "Admin roles" msgstr "" -#: taiga/projects/api.py:188 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:191 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:467 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:497 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "" @@ -1081,7 +1132,7 @@ msgid "Project ID not matches between object and project" msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1091,14 +1142,14 @@ msgstr "" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "" @@ -1113,7 +1164,7 @@ msgstr "" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1127,22 +1178,13 @@ msgstr "" msgid "is deprecated" msgstr "" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "" @@ -1170,16 +1212,6 @@ msgstr "" msgid "Multi-Line Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1339,23 +1371,23 @@ msgstr "" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "" -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "" -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "" -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "" -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "" @@ -1401,10 +1433,10 @@ msgstr "" msgid "external reference" msgstr "" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "" @@ -1416,8 +1448,8 @@ msgstr "" msgid "estimated finish date" msgstr "" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "" @@ -1446,175 +1478,175 @@ msgstr "" msgid "'project' parameter is mandatory" msgstr "" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "" @@ -1646,10 +1678,6 @@ msgstr "" msgid "notify users" msgstr "" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" @@ -1660,7 +1688,7 @@ msgstr "" msgid "Notify exists for specified user and project" msgstr "" -#: taiga/projects/notifications/services.py:397 +#: taiga/projects/notifications/services.py:428 msgid "Invalid value for notify level" msgstr "" @@ -2583,15 +2611,15 @@ msgstr "" msgid "Stakeholder" msgstr "" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:253 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2672,7 +2700,7 @@ msgstr "" msgid "href" msgstr "" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -2724,93 +2752,93 @@ msgstr "" msgid "Invalid image format" msgstr "" -#: taiga/users/api.py:278 +#: taiga/users/api.py:279 msgid "Duplicated email" msgstr "" -#: taiga/users/api.py:280 +#: taiga/users/api.py:281 msgid "Not valid email" msgstr "" -#: taiga/users/api.py:300 taiga/users/api.py:306 +#: taiga/users/api.py:301 taiga/users/api.py:307 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -#: taiga/users/api.py:333 taiga/users/api.py:341 taiga/users/api.py:344 +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" msgstr "" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "" @@ -2822,7 +2850,7 @@ msgstr "" msgid "Invalid username. Try with a different one." msgstr "" -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 12b13166..f48dc2c8 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" @@ -69,6 +69,7 @@ msgid "Error on creating new user." msgstr "Error al crear un nuevo usuario " #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Token inválido" @@ -574,9 +575,9 @@ msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" @@ -823,11 +824,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Tu proyecto ha sido importado" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nombre" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "descripción" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "usuario" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "nombre completo" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "dirección de email" @@ -838,7 +889,7 @@ msgstr "comentario" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -909,8 +960,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "El payload no es un json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "El proyecto no existe" @@ -1092,12 +1143,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Ver proyecto" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Ver sprints" @@ -1106,22 +1157,22 @@ msgid "View user stories" msgstr "Ver historias de usuarios" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Ver tareas" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Ver peticiones" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Ver páginas del wiki" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Ver enlaces del wiki" @@ -1149,135 +1200,119 @@ msgstr "Añadir peticiones" msgid "Add comments to issues" msgstr "Añadir comentarios a peticiones" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Agregar pagina wiki" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Modificar pagina wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Agregar enlace wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Modificar enlace wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Añadir sprint" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Modificar sprint" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Borrar sprint" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Ver historia de usuario" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Agregar historia de usuario" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Modificar historia de usuario" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Borrar historia de usuario" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Agregar tarea" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Modificar tarea" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Borrar tarea" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Añadir petición" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Modificar petición" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Borrar petición" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Borrar pagina wiki" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Borrar enlace wiki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Modificar proyecto" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Agregar miembro" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Eliminar miembro" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Eliminar proyecto" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Administrar valores de proyecto" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Administrar roles" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Nombre de plantilla invalido" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Descripción de plantilla invalida" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Al menos uno de los usuario debe ser un administrador." -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." @@ -1290,7 +1325,7 @@ msgid "Project ID not matches between object and project" msgstr "El ID de proyecto no coincide entre el adjunto y un proyecto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1300,14 +1335,14 @@ msgstr "Dueño" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "Proyecto" @@ -1322,7 +1357,7 @@ msgstr "id de objeto" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1336,22 +1371,13 @@ msgstr "archivo adjunto" msgid "is deprecated" msgstr "está desactualizado" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "descripción" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "orden" @@ -1379,16 +1405,6 @@ msgstr "Texto" msgid "Multi-Line Text" msgstr "Texto multilínea" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "nombre" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1548,23 +1564,23 @@ msgstr "nota de bloqueo" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "No tienes permisos para asignar un sprint a esta petición." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "No tienes permisos para asignar un estado a esta petición." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "No tienes permisos para establecer la gravedad de esta petición." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "No tienes permiso para establecer la prioridad de esta petición." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "No tienes permiso para establecer el tipo de esta petición." @@ -1610,10 +1626,10 @@ msgstr "asignado a" msgid "external reference" msgstr "referencia externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1625,8 +1641,8 @@ msgstr "fecha estimada de comienzo" msgid "estimated finish date" msgstr "fecha estimada de finalización" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "está cerrada" @@ -1657,175 +1673,175 @@ msgstr "el parámetro '{param}' es obligatório" msgid "'project' parameter is mandatory" msgstr "el parámetro 'project' es obligatório" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "email" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "creado el" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "texto extra de la invitación" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "orden del usuario" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "El usuario ya es miembro del proyecto" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "puntos por defecto" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "estado de historia por defecto" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "estado de tarea por defecto" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "prioridad por defecto" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "gravedad por defecto" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "estado de petición por defecto" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "tipo de petición por defecto" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "miembros" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "total de sprints" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "puntos de historia totales" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "panel de backlog activado" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "panel de kanban activado" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "panel de wiki activo" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "panel de peticiones activo" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "sistema de videoconferencia" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "datos extra de videoconferencia" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "creación de plantilla" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "permisos de anónimo" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "permisos de usuario" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "privado" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "colores de etiquetas" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "configuración de modulos" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "archivado" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "color" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "limite del trabajo en progreso" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "rol por defecto para el propietario" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "opciones por defecto" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "estatuas de historias" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "puntos" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "estatus de tareas" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "estados de petición" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "tipos de petición" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "prioridades" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "gravedades" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "roles" @@ -1857,21 +1873,21 @@ msgstr "entradas del histórico" msgid "notify users" msgstr "usuarios notificados" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" -msgstr "" +msgstr "Observado" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "" "Ya existe una política de notificación para este usuario en el proyecto." +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "Valor inválido para el nivel de notificación" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2604,51 +2620,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "No puedes abandonar este proyecto si no existen mas propietarios del mismo" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "La dirección de email ya está en uso." -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Rol inválido para el proyecto" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "El número total de sprints debe ser mayor o igual a cero" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Opciones por defecto" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Estados de historia de usuario" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Puntos" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Estado de tareas" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Estados de peticion" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Tipos de petición" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Gravedades" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Roles" @@ -3080,17 +3096,17 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "No tienes permisos para asignar este sprint a esta historia de usuario." -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" "No tienes permisos para asignar este estado a esta historia de usuario." -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3146,7 +3162,7 @@ msgstr "No existe ningún estado de tarea con este id" #: taiga/projects/votes/models.py:28 msgid "count" -msgstr "" +msgstr "recuento" #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 @@ -3173,7 +3189,7 @@ msgstr "última modificación por" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "Comprueba la API de histórico para obtener el diff exacto" @@ -3189,39 +3205,39 @@ msgstr "Permisos" msgid "Important dates" msgstr "datos importántes" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Nombre de usuario o email no válidos" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "¡Correo enviado con éxito!" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "token inválido" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "La contraseña actual es obligatoria." -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "La nueva contraseña es obligatoria" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "La longitud de la contraseña debe de ser de al menos 6 caracteres" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Contraseña actual inválida" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Argumentos incompletos" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Formato de imagen no válido" @@ -3243,11 +3259,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, ¿estás seguro de que el token es correcto?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "es superusuario" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -3255,24 +3271,24 @@ msgstr "" "Otorga todos los permisos a este usuario sin necesidad de hacerlo " "explicitamente." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "nombre de usuario" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Obligatorio. 30 caracteres o menos. Letras, números y /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Introduce un nombre de usuario válido" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "activo" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -3280,43 +3296,43 @@ msgstr "" "Denota a los usuarios activos. Desmárcalo para dar de baja/borrar a un " "usuario." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografía" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "foto" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "fecha de registro" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "idioma por defecto" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "tema por defecto" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "zona horaria por defecto" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "añade color a las etiquetas" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "token de email" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "nueva dirección de email" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "permisos" @@ -3328,7 +3344,7 @@ msgstr "no válido" msgid "Invalid username. Try with a different one." msgstr "Nombre de usuario inválido. Prueba con otro." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Nombre de usuario o contraseña inválidos." diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index e9173067..809f11a1 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" @@ -67,6 +67,7 @@ msgid "Error on creating new user." msgstr "Virhe käyttäjän luonnissa." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Väärä tunniste" @@ -561,9 +562,9 @@ msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" @@ -807,11 +808,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Projetkisi tiedosto on luettu sisään" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nimi" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "kuvaus" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "koko nimi" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "sähköpostiosoite" @@ -822,7 +873,7 @@ msgstr "kommentti" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -895,8 +946,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "The payload is not a valid json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Projektia ei löydy" @@ -1055,12 +1106,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Katso projektia" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Katso virstapylvästä" @@ -1069,22 +1120,22 @@ msgid "View user stories" msgstr "Katso käyttäjätarinoita" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Katso tehtäviä" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Katso pyyntöjä" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Katso wiki-sivuja" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Katso wiki-linkkejä" @@ -1112,135 +1163,119 @@ msgstr "Lisää pyyntöjä" msgid "Add comments to issues" msgstr "Lisää kommentteja pyyntöihin" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Lisää wiki-sivu" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Muokkaa wiki-sivua" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Lisää wiki-linkki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Muokkaa wiki-linkkiä" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Lisää virstapylväs" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Muokkaa virstapyvästä" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Poista virstapylväs" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Katso käyttäjätarinaa" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Lisää käyttäjätarina" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Muokkaa käyttäjätarinaa" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Poista käyttäjätarina" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Lisää tehtävä" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Muokkaa tehtävää" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Poista tehtävä" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Lisää pyyntö" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Muokkaa pyyntöä" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Poista pyyntö" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Poista wiki-sivu" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Poista wiki-linkki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Muokkaa projekti" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Lisää jäsen" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Poista jäsen" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Poista projekti" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Hallinnoi projektin arvoja" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Hallinnoi rooleja" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Virheellinen mallipohjan nimi" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Virheellinen mallipohjan kuvaus" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Vähintään yhden käyttäjän pitää olla aktiivinen ylläpitäjä" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." @@ -1253,7 +1288,7 @@ msgid "Project ID not matches between object and project" msgstr "Projekti ID ei vastaa kohdetta ja projektia" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1263,14 +1298,14 @@ msgstr "omistaja" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "projekti" @@ -1285,7 +1320,7 @@ msgstr "objekti ID" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1299,22 +1334,13 @@ msgstr "liite" msgid "is deprecated" msgstr "on poistettu" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "kuvaus" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "order" @@ -1342,16 +1368,6 @@ msgstr "" msgid "Multi-Line Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "nimi" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1511,23 +1527,23 @@ msgstr "suljettu muistiinpano" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "Sinulla ei ole oikeuksia laittaa kierrosta tälle pyynnölle." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "Sinulla ei ole oikeutta asettaa statusta tälle pyyntö." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "Sinulla ei ole oikeutta asettaa vakavuutta tälle pyynnölle." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "Sinulla ei ole oikeutta asettaa kiireellisyyttä tälle pyynnölle." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Sinulla ei ole oikeutta asettaa tyyppiä tälle pyyntö." @@ -1573,10 +1589,10 @@ msgstr "tekijä" msgid "external reference" msgstr "ulkoinen viittaus" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "hukka-aika" @@ -1588,8 +1604,8 @@ msgstr "arvioitu alkupvm" msgid "estimated finish date" msgstr "arvioitu loppupvm" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "on suljettu" @@ -1618,175 +1634,175 @@ msgstr "'{param}' parametri on pakollinen" msgid "'project' parameter is mandatory" msgstr "'project' parametri on pakollinen" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "sähköposti" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "luo täällä" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "tunniste" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "kutsun lisäteksti" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "käyttäjäjärjestys" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "Käyttäjä on jo projektin jäsen" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "oletuspisteet" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "oletus Kt tila" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "oletus tehtävän tila" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "oletus kiireellisyys" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "oletus vakavuus" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "oletus pyynnön tila" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "oletus pyyntö tyyppi" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "jäsenet" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "virstapyväitä yhteensä" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "käyttäjätarinan yhteispisteet" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "aktiivinen odottavien paneeli" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "aktiivinen kanban-paneeli" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "aktiivinen wiki-paneeli" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "aktiivinen pyyntöpaneeli" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "videokokous järjestelmä" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "luo mallipohja" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "vieraan oikeudet" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "käyttäjän oikeudet" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "on yksityinen" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "avainsanojen värit" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "moduulien asetukset" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "on arkistoitu" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "väri" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "työn alla olevien max" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "arvo" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "oletus omistajan rooli" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "oletus optiot" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "kt tilat" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pisteet" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "tehtävän tilat" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "pyyntöjen tilat" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "pyyntötyypit" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "kiireellisyydet" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "vakavuudet" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "roolit" @@ -1818,20 +1834,20 @@ msgstr "historian kohteet" msgid "notify users" msgstr "ilmoita käyttäjille" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "Ilmoita olemassaolosta määritellyille käyttäjille ja projektille" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2573,51 +2589,51 @@ msgstr "versio" msgid "You can't leave the project if there are no more owners" msgstr "Et voi jättää projektia, jos olet ainoa omistaja" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Sähköpostiosoite on jo käytössä" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Virheellinen rooli projektille" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Virstapylväitä yhteensä pitää olla vähintään 0." -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Oletusoptiot" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Käyttäjätarinatilat" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Pisteet" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Tehtävien tilat" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Pyyntöjen tilat" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "pyyntötyypit" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Kiireellisyydet" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Vakavuudet" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Roolit" @@ -3046,15 +3062,15 @@ msgstr "Tuoteomistaja" msgid "Stakeholder" msgstr "Sidosryhmä" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3137,7 +3153,7 @@ msgstr "viimeksi muokannut" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -3153,39 +3169,39 @@ msgstr "Oikeudet" msgid "Important dates" msgstr "Tärkeät päivämäärät" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Tuntematon käyttäjänimi tai sähköposti" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "Sähköposti lähetetty." -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Tunniste on virheellinen" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Nykyinen salasanaparametri tarvitaan" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Uusi salasanaparametri tarvitaan" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Salasanan pitää olla vähintään 6 merkkiä pitkä" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Virheellinen nykyinen salasana" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Puutteelliset argumentit" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Väärä kuvaformaatti" @@ -3208,80 +3224,80 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Virheellinen, oletko varma että tunniste on oikea?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "pääkäyttäjän status" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "" "Kertoo että käyttäjä saa tehdä kaiken ilman erikseen annettuja oiekuksia." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "käyttäjänimi" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "" "Vaaditaan. Korkeintaan 30merkkiä. Kirjaimet, numerot ja merkit /./-/_ " "sallittuja" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Anna olemassa oleva käyttäjänimi." -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "aktiivinen" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "" "Käyttäjä on aktiivinen. Poista aktiivisuus käyttäjän poistamisen sijaan." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografia" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "kuva" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "liittymispvm" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "oletuskieli" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "oletus aikavyöhyke" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "väritä avainsanat" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "sähköpostitunniste" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "uusi sähköpostiosoite" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "oikeudet" @@ -3293,7 +3309,7 @@ msgstr "virheellinen" msgid "Invalid username. Try with a different one." msgstr "Tuntematon käyttäjänimi, yritä uudelleen." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Käyttäjätunnus tai salasana eivät ole oikein." diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index c79bfb32..83837145 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -9,6 +9,7 @@ # Florent B. , 2015 # Louis-Michel Couture , 2015 # Matthieu Durocher , 2015 +# naekos , 2015 # Nlko , 2015 # Stéphane Mor , 2015 # William Godin , 2015 @@ -16,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" @@ -74,6 +75,7 @@ msgid "Error on creating new user." msgstr "Erreur à la création du nouvel utilisateur." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Jeton invalide" @@ -590,9 +592,9 @@ msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" @@ -795,11 +797,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Votre projet à été importé" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nom" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "description" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "utilisateur" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "Nom complet" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "Adresse email" @@ -810,7 +862,7 @@ msgstr "Commentaire" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -881,8 +933,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Le payload n'est pas un json valide" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Le projet n'existe pas" @@ -1026,12 +1078,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Consulter le projet" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Voir les jalons" @@ -1040,22 +1092,22 @@ msgid "View user stories" msgstr "Voir les histoires utilisateur" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Consulter les tâches" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Voir les problèmes" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Consulter les pages Wiki" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Consulter les liens Wiki" @@ -1083,135 +1135,119 @@ msgstr "Ajouter des problèmes" msgid "Add comments to issues" msgstr "Ajouter des commentaires aux problèmes" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Ajouter une page Wiki" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Modifier une page Wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Ajouter un lien Wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Modifier un lien Wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Ajouter un jalon" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Modifier le jalon" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Supprimer le jalon" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Voir l'histoire utilisateur" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Ajouter une histoire utilisateur" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Modifier l'histoire utilisateur" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Supprimer l'histoire utilisateur" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Ajouter une tâche" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Modifier une tâche" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Supprimer une tâche" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Ajouter un problème" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Modifier le problème" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Supprimer le problème" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Supprimer une page Wiki" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Supprimer un lien Wiki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Modifier le projet" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Ajouter un membre" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Supprimer un membre" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Supprimer le projet" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Administrer les paramètres du projet" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Administrer les rôles" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Nom de modèle non valide" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Description du modèle non valide" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Au moins un utilisateur doit être un administrateur actif" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" @@ -1224,7 +1260,7 @@ msgid "Project ID not matches between object and project" msgstr "L'identifiant du projet de correspond pas entre l'objet et le projet" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1234,14 +1270,14 @@ msgstr "propriétaire" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "projet" @@ -1256,7 +1292,7 @@ msgstr "identifiant de l'objet" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1270,22 +1306,13 @@ msgstr "pièces jointes" msgid "is deprecated" msgstr "est obsolète" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "description" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "ordre" @@ -1313,16 +1340,6 @@ msgstr "Texte" msgid "Multi-Line Text" msgstr "Texte multi-ligne" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "nom" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1482,23 +1499,23 @@ msgstr "note bloquée" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "Vous n'avez pas la permission d'affecter ce sprint à ce problème." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "Vous n'avez pas la permission d'affecter ce statut à ce problème." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "Vous n'avez pas la permission d'affecter cette sévérité à ce problème." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "Vous n'avez pas la permission d'affecter cette priorité à ce problème." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Vous n'avez pas la permission d'affecter ce type à ce problème." @@ -1544,10 +1561,10 @@ msgstr "assigné à" msgid "external reference" msgstr "référence externe" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1559,8 +1576,8 @@ msgstr "date de démarrage estimée" msgid "estimated finish date" msgstr "date de fin estimée" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "est fermé" @@ -1589,175 +1606,175 @@ msgstr "'{param}' paramètre obligatoire" msgid "'project' parameter is mandatory" msgstr "'project' paramètre obligatoire" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "email" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "Créé le" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "jeton" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "Text supplémentaire de l'invitation" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "classement utilisateur" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "L'utilisateur est déjà un membre du projet" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "Points par défaut" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "statut de l'HU par défaut" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "Etat par défaut des tâches" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "Priorité par défaut" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "Sévérité par défaut" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "statut du problème par défaut" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "type de problème par défaut" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "membres" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "total des jalons" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "total des points d'histoire" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "panneau backlog actif" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "panneau kanban actif" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "panneau wiki actif" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "panneau problèmes actif" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "plateforme de vidéoconférence" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "Modèle de création" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "Permissions anonymes" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "Permission de l'utilisateur" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "est privé" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "couleurs des tags" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "Configurations des modules" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "est archivé" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "couleur" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "limite de travail en cours" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "valeur" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "rôle par défaut du propriétaire" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "options par défaut" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "statuts des us" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "points" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "états des tâches" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "statuts des problèmes" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "types de problèmes" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "priorités" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "sévérités" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "rôles" @@ -1789,20 +1806,20 @@ msgstr "entrées dans l'historique" msgid "notify users" msgstr "notifier les utilisateurs" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "La notification existe pour l'utilisateur et le projet spécifiés" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2317,51 +2334,51 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "Vous ne pouvez pas quitter le projet si il n'y a plus d'autres propriétaires" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Adresse email déjà existante" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Rôle non valide pour le projet" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Le nombre de jalons doit être supérieur ou égal à zéro" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Options par défaut" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Etats de la User Story" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Points" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Etats des tâches" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Statuts des problèmes" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Types de problèmes" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Priorités" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Sévérités" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Rôles" @@ -2778,15 +2795,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Participant" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2869,7 +2886,7 @@ msgstr "dernier modificateur" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -2885,39 +2902,39 @@ msgstr "Permissions" msgid "Important dates" msgstr "Dates importantes" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Nom d'utilisateur ou email non valide" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "Mail envoyé avec succès!" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Jeton invalide" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Paramètre 'mot de passe actuel' requis" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Paramètre 'nouveau mot de passe' requis" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Le mot de passe doit être d'au moins 6 caractères" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Mot de passe actuel incorrect" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "arguments manquants" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "format de l'image non valide" @@ -2940,11 +2957,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Invalide, êtes-vous sûre que le jeton est correct ?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "statut superutilisateur" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -2952,25 +2969,25 @@ msgstr "" "Indique que l'utilisateur a toutes les permissions sans avoir à lui les " "donner explicitement" -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "nom d'utilisateur" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "" "Obligatoire. 30 caractères maximum. Lettres, nombres et les caractères /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Entrez un nom d'utilisateur valide" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "actif" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -2978,43 +2995,43 @@ msgstr "" "Indique qu'un utilisateur est considéré ou non comme actif. Désélectionnez " "cette option au lieu de supprimer le compte utilisateur." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biographie" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "photo" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "date d'inscription" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "langage par défaut" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "thème par défaut" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "Fuseau horaire par défaut" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "changer la couleur des tags" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "jeton email" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "nouvelle adresse email" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "permissions" @@ -3026,7 +3043,7 @@ msgstr "invalide" msgid "Invalid username. Try with a different one." msgstr "Nom d'utilisateur invalide. Essayez avec un autre nom." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Aucun utilisateur avec ce nom ou ce mot de passe." diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 5571d643..8ca843d5 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" @@ -66,6 +66,7 @@ msgid "Error on creating new user." msgstr "Fout bij het aanmaken van een nieuwe gebruiker." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Ongeldig token" @@ -574,9 +575,9 @@ msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" @@ -757,11 +758,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Je project dump is geïmporteerd" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "naam" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "omschrijving" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "volledige naam" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "e-mail adres" @@ -772,7 +823,7 @@ msgstr "commentaar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -844,8 +895,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "De payload is geen geldige json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Het project bestaat niet" @@ -989,12 +1040,12 @@ msgid "" msgstr "" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Bekijk project" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Bekijk milestones" @@ -1003,22 +1054,22 @@ msgid "View user stories" msgstr "Bekijk user stories" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Bekijk taken" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Bekijk issues" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Bekijk wiki pagina's" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Bekijk wiki links" @@ -1046,135 +1097,119 @@ msgstr "Voeg issues toe" msgid "Add comments to issues" msgstr "Voeg commentaar toe aan issues" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Voeg wiki pagina toe" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Wijzig wiki pagina" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Voeg wiki link toe" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Wijzig wiki link" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Voeg milestone toe" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Wijzig milestone" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Verwijder milestone" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Bekijk user story" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Voeg user story toe" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Wijzig user story" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Verwijder user story" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Voeg taak toe" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Wijzig taak" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Verwijder taak" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Voeg issue toe" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Wijzig issue" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Verwijder issue" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Verwijder wiki pagina" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Verwijder wiki link" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Wijzig project" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Voeg lid toe" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Verwijder lid" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Verwijder project" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Admin project waarden" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Admin rollen" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Ongeldige template naam" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Ongeldige template omschrijving" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Minstens één van de gebruikers moet een active admin zijn" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." @@ -1187,7 +1222,7 @@ msgid "Project ID not matches between object and project" msgstr "Project ID van object is niet gelijk aan die van het project" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1197,14 +1232,14 @@ msgstr "eigenaar" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "project" @@ -1219,7 +1254,7 @@ msgstr "object id" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1233,22 +1268,13 @@ msgstr "bijgevoegd bestand" msgid "is deprecated" msgstr "is verouderd" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "omschrijving" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "volgorde" @@ -1276,16 +1302,6 @@ msgstr "" msgid "Multi-Line Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "naam" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1445,25 +1461,25 @@ msgstr "geblokkeerde notitie" msgid "sprint" msgstr "" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "Je hebt geen toestemming om deze sprint op deze issue te zetten." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "Je hebt geen toestemming om deze status toe te kennen aan dze issue." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "" "Je hebt geen toestemming om dit ernstniveau toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "" "Je hebt geen toestemming om deze prioriteit toe te kennen aan deze issue." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Je hebt geen toestemming om dit type toe te kennen aan deze issue." @@ -1509,10 +1525,10 @@ msgstr "toegewezen aan" msgid "external reference" msgstr "externe referentie" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1524,8 +1540,8 @@ msgstr "geschatte start datum" msgid "estimated finish date" msgstr "geschatte datum van afwerking" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "is gesloten" @@ -1554,175 +1570,175 @@ msgstr "'{param}' parameter is verplicht" msgid "'project' parameter is mandatory" msgstr "'project' parameter is verplicht" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "e-mail" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "aangemaakt op" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "uitnodiging extra text" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "gebruiker volgorde" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "The gebruikers is al lid van het project" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "standaard punten" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "standaard US status" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "default taak status" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "standaard prioriteit" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "standaard ernstniveau" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "standaard issue status" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "standaard issue type" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "leden" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "totaal van de milestones" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "totaal story points" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "actief backlog paneel" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "actief kanban paneel" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "actief wiki paneel" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "actief issues paneel" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "videoconference systeem" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "aanmaak template" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "anonieme toestemmingen" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "gebruikers toestemmingen" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "is privé" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "tag kleuren" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "module config" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "is gearchiveerd" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "kleur" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "work in progress limiet" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "waarde" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "standaard rol eigenaar" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "standaard instellingen" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "us statussen" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "punten" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "taak statussen" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "issue statussen" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "issue types" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "prioriteiten" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "ernstniveaus" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "rollen" @@ -1754,20 +1770,20 @@ msgstr "geschiedenis items" msgid "notify users" msgstr "verwittig gebruikers" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "Verwittiging bestaat voor gespecifieerde gebruiker en project" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2293,51 +2309,51 @@ msgstr "versie" msgid "You can't leave the project if there are no more owners" msgstr "Je kan het project niet verlaten als er geen andere eigenaars zijn" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "E-mail adres is al in gebruik" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Ongeldige rol voor project" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Totaal milestones moet groter of gelijk zijn aan 0" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Standaard opties" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Status van User story" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Punten" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Statussen van taken" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Statussen van Issues" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Types van issue" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Prioriteiten" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Ernstniveaus" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Rollen" @@ -2741,15 +2757,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -2832,7 +2848,7 @@ msgstr "gebruiker met laatste wijziging" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -2848,39 +2864,39 @@ msgstr "Toestemmingen" msgid "Important dates" msgstr "Belangrijke data" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Ongeldige gebruikersnaam of e-mail" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "Mail met succes verzonden!" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Token is ongeldig" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Huidig wachtwoord parameter vereist" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Nieuw wachtwoord parameter vereist" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Ongeldige lengte van wachtwoord, minstens 6 tekens vereist" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Ongeldig huidig wachtwoord" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Onvolledige argumenten" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Ongeldig afbeelding formaat" @@ -2901,11 +2917,11 @@ msgstr "Ongeldig, weet je zeker dat het token correct en ongebruikt is?" msgid "Invalid, are you sure the token is correct?" msgstr "Ongeldig, weet je zeker dat het token correct is?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "superuser status" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -2913,24 +2929,24 @@ msgstr "" "Beduidt dat deze gebruik alle toestemmingen heeft zonder deze expliciet toe " "te wijzen." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "gebruikersnaam" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Vereist. 30 of minder karakters. Letters, nummers en /./-/_ karakters" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Geef een geldige gebruikersnaam in" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "actief" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -2938,43 +2954,43 @@ msgstr "" "Beduidt of deze gebruiker als actief moet behandeld worden. Deselecteer dit " "i.p.v. accounts te verwijderen." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografie" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "foto" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "toetrededatum" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "standaard taal" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "standaard tijdzone" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "kleur tags" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "e-mail token" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "nieuw e-mail adres" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "toestemmingen" @@ -2986,7 +3002,7 @@ msgstr "ongeldig" msgid "Invalid username. Try with a different one." msgstr "Ongeldige gebruikersnaam. Probeer met een andere." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Gebruikersnaam of wachtwoord stemt niet overeen met gebruiker." diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 2a2ebba4..4dfde0cd 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-09-04 20:54+0000\n" -"Last-Translator: Wiktor Żurawik \n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -67,6 +67,7 @@ msgid "Error on creating new user." msgstr "Błąd przy tworzeniu użytkownika." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Nieprawidłowy token" @@ -574,9 +575,9 @@ msgid "It contain invalid custom fields." msgstr "Zawiera niewłaściwe pola niestandardowe." #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" @@ -824,11 +825,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Twój zrzut projektu został prawidłowo zaimportowany" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nazwa" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "opis" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "użytkownik" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "Imię i Nazwisko" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "adres e-mail" @@ -839,7 +890,7 @@ msgstr "komentarz" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -911,8 +962,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Źródło nie jest prawidłowym plikiem json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Projekt nie istnieje" @@ -1096,12 +1147,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Zobacz projekt" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Zobacz kamienie milowe" @@ -1110,22 +1161,22 @@ msgid "View user stories" msgstr "Zobacz historyjki użytkownika" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Zobacz zadania" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Zobacz zgłoszenia" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Zobacz strony Wiki" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Zobacz linki Wiki" @@ -1153,135 +1204,119 @@ msgstr "Dodaj zgłoszenia" msgid "Add comments to issues" msgstr "Dodaj komentarze do zgłoszeń" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Dodaj strony Wiki" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Modyfikuj stronę Wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Dodaj link do Wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Modyfikuj link do Wiki" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "Dodaj projekt od ulubionych" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "Zagłosuj na historyjkę użytkownika" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "Zagłosuj na zadanie" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "Zagłosuj na zgłoszenie" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Dodaj kamień milowy" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Modyfikuj Kamień milowy" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Usuń kamień milowy" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Zobacz historyjkę użytkownika" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Dodaj historyjkę użytkownika" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Modyfikuj historyjkę użytkownika" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Usuń historyjkę użytkownika" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Dodaj zadanie" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Modyfikuj zadanie" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Usuń zadanie" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Dodaj zgłoszenie" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Modyfikuj zgłoszenie" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Usuń zgłoszenie" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Usuń stronę Wiki" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Usuń link Wiki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Modyfikuj projekt" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Dodaj członka zespołu" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Usuń członka zespołu" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Usuń projekt" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Administruj wartościami projektu" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Administruj rolami" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Nieprawidłowa nazwa szablonu" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Nieprawidłowy opis szablonu" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Przynajmniej jeden użytkownik musi być aktywnym Administratorem" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Nie masz uprawnień by to zobaczyć." @@ -1294,7 +1329,7 @@ msgid "Project ID not matches between object and project" msgstr "ID nie pasuje pomiędzy obiektem a projektem" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1304,14 +1339,14 @@ msgstr "właściciel" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "projekt" @@ -1326,7 +1361,7 @@ msgstr "id obiektu" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1340,22 +1375,13 @@ msgstr "załączony plik" msgid "is deprecated" msgstr "jest przestarzałe" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "opis" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "kolejność" @@ -1383,16 +1409,6 @@ msgstr "Tekst" msgid "Multi-Line Text" msgstr "Teks wielowierszowy" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "nazwa" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1552,23 +1568,23 @@ msgstr "zaglokowana notatka" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "Nie masz uprawnień do połączenia tego zgłoszenia ze sprintem." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "Nie masz uprawnień do ustawienia statusu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "Nie masz uprawnień do ustawienia ważności dla tego zgłoszenia." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "Nie masz uprawnień do ustawienia priorytetu dla tego zgłoszenia." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Nie masz uprawnień do ustawienia typu dla tego zgłoszenia." @@ -1614,10 +1630,10 @@ msgstr "przypisane do" msgid "external reference" msgstr "źródło zgłoszenia" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1629,8 +1645,8 @@ msgstr "szacowana data rozpoczecia" msgid "estimated finish date" msgstr "szacowana data zakończenia" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "jest zamknięte" @@ -1659,175 +1675,175 @@ msgstr "'{param}' parametr jest obowiązkowy" msgid "'project' parameter is mandatory" msgstr "'project' parametr jest obowiązkowy" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "e-mail" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "utwórz na" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "dodatkowy tekst w zaproszeniu" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "kolejność użytkowników" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "Użytkownik już jest członkiem tego projektu" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "domyślne punkty" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "domyślny status dla HU" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "domyślny status dla zadania" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "domyślny priorytet" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "domyślna ważność" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "domyślny status dla zgłoszenia" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "domyślny typ dla zgłoszenia" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "członkowie" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "wszystkich kamieni milowych" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "wszystkich punktów " -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "aktywny panel backlog" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "aktywny panel Kanban" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "aktywny panel Wiki" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "aktywny panel zgłoszeń " -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "system wideokonferencji" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "dodatkowe dane dla wideokonferencji" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "szablon " -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "uprawnienia anonimowych" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "uprawnienia użytkownika" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "jest prywatna" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "kolory tagów" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "konfiguracja modułów" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "zarchiwizowane" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "kolor" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "limit postępu prac" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "wartość" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "domyśla rola właściciela" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "domyślne opcje" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "statusy HU" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pinkty" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "statusy zadań" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "statusy zgłoszeń" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "typy zgłoszeń" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "priorytety" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "ważność" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "role" @@ -1859,20 +1875,20 @@ msgstr "wpisy historii" msgid "notify users" msgstr "powiadom użytkowników" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "użytkownik" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "Obserwowane" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "Powiadomienie istnieje dla określonego użytkownika i projektu" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "Nieprawidłowa wartość dla poziomu notyfikacji" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2629,51 +2645,51 @@ msgstr "wersja" msgid "You can't leave the project if there are no more owners" msgstr "Nie możesz opuścić projektu, jeśli jesteś jego jedynym właścicielem" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Tena adres e-mail jest już w użyciu" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Nieprawidłowa rola w projekcie" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Łączna liczba kamieni milowych musi być większa lub równa zero" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Domyślne opcje" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Statusy historyjek użytkownika" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Punkty" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Statusy zadań" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Statusy zgłoszeń" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Typu zgłoszeń" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Priorytety" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Ważność" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Role" @@ -3105,17 +3121,17 @@ msgstr "Właściciel produktu" msgid "Stakeholder" msgstr "Interesariusz" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "Nie masz uprawnień do ustawiania sprintu dla tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" "Nie masz uprawnień do ustawiania statusu do tej historyjki użytkownika." -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3198,7 +3214,7 @@ msgstr "ostatnio zmodyfikowane przez" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "Dla pełengo diffa sprawdź API historii" @@ -3214,41 +3230,41 @@ msgstr "Uprawnienia" msgid "Important dates" msgstr "Ważne daty" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Nieprawidłowa nazwa użytkownika lub adrs e-mail" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "E-mail wysłany poprawnie!" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Nieprawidłowy token." -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Należy podać bieżące hasło" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Należy podać nowe hasło" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "" "Nieprawidłowa długość hasła - wymagane jest co najmniej 6 znaków" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Podałeś nieprawidłowe bieżące hasło" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Pola niekompletne" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Niepoprawny format obrazka" @@ -3271,11 +3287,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Niepoprawne, jesteś pewien, że token jest poprawny?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "status SUPERUSER" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -3283,24 +3299,24 @@ msgstr "" "Oznacza, że ten użytkownik posiada wszystkie uprawnienia bez konieczności " "ich przydzielania." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "nazwa użytkownika" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Wymagane. 30 znaków. Liter, cyfr i znaków /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Wprowadź poprawną nazwę użytkownika" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "aktywny" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -3308,43 +3324,43 @@ msgstr "" "Oznacza, że ten użytkownik ma być traktowany jako aktywny. Możesz to " "odznaczyć zamiast usuwać konto." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografia" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "zdjęcie" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "data dołączenia" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "domyślny język Taiga" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "domyślny szablon Taiga" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "domyśla strefa czasowa" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "kolory tagów" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "tokem e-mail" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "nowy adres e-mail" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "uprawnienia" @@ -3356,7 +3372,7 @@ msgstr "Niepoprawne" msgid "Invalid username. Try with a different one." msgstr "Niepoprawna nazwa użytkownika. Spróbuj podać inną." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Nazwa użytkownika lub hasło są nieprawidłowe" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 3ab2a23c..6b2f5701 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -6,6 +6,7 @@ # Cléber Zavadniak , 2015 # Thiago , 2015 # Daniel Dias , 2015 +# Hevertton Barbosa , 2015 # Kemel Zaidan , 2015 # Marlon Carvalho , 2015 # Renato Prado , 2015 @@ -14,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-09-02 14:50+0000\n" -"Last-Translator: Daniel Dias \n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -71,6 +72,7 @@ msgid "Error on creating new user." msgstr "Erro ao criar um novo usuário." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Token inválido" @@ -578,9 +580,9 @@ msgid "It contain invalid custom fields." msgstr "Contém campos personalizados inválidos" #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nome duplicado para o projeto" @@ -827,11 +829,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] A restauração do seu projeto foi importada" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "Nome" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "descrição" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "usuário" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "nome completo" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "endereço de e-mail" @@ -842,7 +894,7 @@ msgstr "comentário" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -914,8 +966,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "O carregamento não é um json válido" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "O projeto não existe" @@ -1098,12 +1150,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Ver projeto" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Ver marco de progresso" @@ -1112,22 +1164,22 @@ msgid "View user stories" msgstr "Ver user stories" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Ver tarefa" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Ver casos" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Ver página wiki" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Ver links wiki" @@ -1155,135 +1207,119 @@ msgstr "Adicionar casos" msgid "Add comments to issues" msgstr "Adicionar comentários aos casos" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Adicionar página wiki" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "modificar página wiki" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Adicionar link wiki" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Modificar wiki link" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Adicionar marco de progresso" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "modificar marco de progresso" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "remover marco de progresso" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Ver user story" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Adicionar user story" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Modificar user story" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Deletar user story" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Adicionar tarefa" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Modificar tarefa" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Deletar tarefa" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Adicionar caso" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Modificar caso" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Deletar caso" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Deletar página wiki" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Deletar link wiki" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Modificar projeto" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "adicionar membro" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Remover membro" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Deletar projeto" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Valores projeto admin" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Funções Admin" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Nome de template inválido" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Descrição de template inválida" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "Pelo menos one dos usuários deve ser um administrador ativo" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Você não tem permissão para ver isso" @@ -1296,7 +1332,7 @@ msgid "Project ID not matches between object and project" msgstr "Project ID não encontrado entre objeto e projeto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1306,14 +1342,14 @@ msgstr "Dono" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "projeto" @@ -1328,7 +1364,7 @@ msgstr "identidade de objeto" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1342,22 +1378,13 @@ msgstr "Arquivo anexado" msgid "is deprecated" msgstr "está obsoleto" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "descrição" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "ordem" @@ -1385,16 +1412,6 @@ msgstr "Texto" msgid "Multi-Line Text" msgstr "Multi-linha" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "Nome" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1554,23 +1571,23 @@ msgstr "nota bloqueada" msgid "sprint" msgstr "sprint" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "Você não tem permissão para colocar esse sprint para esse caso." -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "Você não tem permissão para colocar esse status para esse caso." -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "Você não tem permissão para colocar essa severidade para esse caso." -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "Você não tem permissão para colocar essa prioridade para esse caso." -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "Você não tem permissão para colocar esse tipo para esse caso." @@ -1616,10 +1633,10 @@ msgstr "Assinado a" msgid "external reference" msgstr "referência externa" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1631,8 +1648,8 @@ msgstr "Data de início estimada" msgid "estimated finish date" msgstr "data de encerramento estimada" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "está fechado" @@ -1661,175 +1678,175 @@ msgstr "'{param}' parametro é mandatório" msgid "'project' parameter is mandatory" msgstr "'project' parametro é mandatório" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "email" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "criado em" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "token" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "texto extra de convite" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "ordem de usuário" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "O usuário já é membro do projeto" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "pontos padrão" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "status de US padrão" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "status padrão de tarefa" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "prioridade padrão" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "severidade padrão" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "status padrão de caso" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "tipo padrão de caso" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "members" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "total de marcos de progresso" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "pontos totáis de US" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "painel de backlog ativo" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "painel de kanban ativo" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "painel de wiki ativo" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "painel de casos ativo" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "sistema de videoconferencia" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "informação extra de videoconferencia" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "template de criação" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "permissão anônima" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "permissão de usuário" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "é privado" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "cores de tags" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "configurações de modulos" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "está arquivado" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "cor" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "trabalho no limite de progresso" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "valor" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "função padrão para dono " -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "opções padrão" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "status de US" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "pontos" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "status de tarefa" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "status de casos" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "tipos de caso" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "prioridades" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "severidades" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "funções" @@ -1861,20 +1878,20 @@ msgstr "histórico de entradas" msgid "notify users" msgstr "notificar usuário" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "usuário" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" -msgstr "" +msgstr "Observado" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "notificação exist para usuário específico e projeto" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "Valor inválido para nível de notificação" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2611,51 +2628,51 @@ msgstr "versão" msgid "You can't leave the project if there are no more owners" msgstr "Você não pode deixar o projeto se não há mais donos" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Endereço de e-mail já utilizado" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Função inválida para projeto" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Total de marcos de progresso devem ser maior ou igual a zero" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Opções padrão" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Status de US" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Pontos" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Status de tarefas" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Status de casos" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Tipos de casos" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Severidades" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Funções" @@ -3088,15 +3105,15 @@ msgstr "Product Owner" msgid "Stakeholder" msgstr "Stakeholder" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "Você não tem permissão para colocar esse sprint para essa user story." -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "Você não tem permissão para colocar esse status para essa user story." -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3179,7 +3196,7 @@ msgstr "último modificador" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "Verifique o histórico da API para a exata diferença" @@ -3195,39 +3212,39 @@ msgstr "Permissões" msgid "Important dates" msgstr "Datas importantes" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "usuário ou e-mail inválido" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "E-mail enviado com sucesso" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "Token é inválido" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "parâmetro de senha atual necessário" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Parâmetro de nova senha necessário" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "senha atual inválida" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "argumentos incompletos" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "formato de imagem inválida" @@ -3250,11 +3267,11 @@ msgstr "" msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, está certo que o token está correto?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "status de superuser" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." @@ -3262,24 +3279,24 @@ msgstr "" "Designa que esse usuário tem todas as permissões sem explicitamente assiná-" "las" -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "usuário" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Requerido. 30 caracteres ou menos. Letras, números e caracteres /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Digite um usuário válido" -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "ativo" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." @@ -3287,43 +3304,43 @@ msgstr "" "Designa quando esse usuário deve ser tratado como ativo. desmarque isso em " "vez de deletar contas." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "biografia" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "foto" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "data ingressado" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "lingua padrão" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "tema padrão" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "fuso horário padrão" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "tags coloridas" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "token de e-mail" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "novo endereço de email" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "permissões" @@ -3335,7 +3352,7 @@ msgstr "inválido" msgid "Invalid username. Try with a different one." msgstr "Usuário inválido. Tente com um diferente." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Usuário ou senha não correspondem ao usuário" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 95daac8e..6225928d 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the taiga-back package. # # Translators: +# dmitriy , 2015 # Dmitry Lobanov , 2015 # Dmitry Vinokurov , 2015 # Марат , 2015 @@ -10,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-30 17:58+0000\n" -"Last-Translator: Dmitry Vinokurov \n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" @@ -69,6 +70,7 @@ msgid "Error on creating new user." msgstr "Ошибка при создании нового пользователя." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "Неверный идентификатор" @@ -580,9 +582,9 @@ msgid "It contain invalid custom fields." msgstr "Содержит неверные специальные поля" #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" @@ -828,11 +830,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] Свалочный файл вашего проекта импортирован" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "имя" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "описание" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "пользователь" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "полное имя" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "адрес email" @@ -843,7 +895,7 @@ msgstr "комментарий" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -915,8 +967,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "Нагрузочный файл не является правильным json-файлом" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "Проект не существует" @@ -1100,12 +1152,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "Просмотреть проект" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "Просмотреть вехи" @@ -1114,22 +1166,22 @@ msgid "View user stories" msgstr "Просмотреть пользовательские истории" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "Просмотреть задачи" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "Посмотреть задачи" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "Просмотреть wiki-страницы" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "Просмотреть wiki-ссылки" @@ -1157,136 +1209,120 @@ msgstr "Добавить задачи" msgid "Add comments to issues" msgstr "Добавить комментарии к задачам" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "Создать wiki-страницу" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "Изменить wiki-страницу" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "Добавить wiki-ссылку" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "Изменить wiki-ссылку" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "Отметить проект" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "Проголосовать за пользовательскую историю" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "Проголосовать за задачу" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "Проголосовать за задачу" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "Добавить веху" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "Изменить веху" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "Удалить веху" -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "Просмотреть пользовательскую историю" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "Добавить пользовательскую историю" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "Изменить пользовательскую историю" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "Удалить пользовательскую историю" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "Добавить задачу" -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "Изменить задачу" -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "Удалить задачу" -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "Добавить задачу" -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "Изменить задачу" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "Удалить задачу" -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "Удалить wiki-страницу" -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "Удалить wiki-ссылку" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "Изменить проект" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "Добавить участника" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "Удалить участника" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "Удалить проект" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "Управлять значениями проекта" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "Управлять ролями" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "Неверное название шаблона" -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "Неверное описание шаблона" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "" "По крайней мере один пользователь должен быть активным администратором." -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "У вас нет разрешения на просмотр." @@ -1299,7 +1335,7 @@ msgid "Project ID not matches between object and project" msgstr "Идентификатор проекта не подходит к этому объекту" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1309,14 +1345,14 @@ msgstr "владелец" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "проект" @@ -1331,7 +1367,7 @@ msgstr "идентификатор объекта" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1345,22 +1381,13 @@ msgstr "приложенный файл" msgid "is deprecated" msgstr "устаревшее" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "описание" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "порядок" @@ -1388,16 +1415,6 @@ msgstr "Текст" msgid "Multi-Line Text" msgstr "Многострочный текст" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "имя" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1557,25 +1574,25 @@ msgstr "Заметка о блокировке" msgid "sprint" msgstr "спринт" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "У вас нет прав для того чтобы установить такой спринт для этой задачи" -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "У вас нет прав для того чтобы установить такой статус для этой задачи" -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "" "У вас нет прав для того чтобы установить такую важность для этой задачи" -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "" "У вас нет прав для того чтобы установить такой приоритет для этой задачи" -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "У вас нет прав для того чтобы установить такой тип для этой задачи" @@ -1621,10 +1638,10 @@ msgstr "назначено" msgid "external reference" msgstr "внешняя ссылка" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "ссылочное имя" @@ -1636,8 +1653,8 @@ msgstr "предполагаемая дата начала" msgid "estimated finish date" msgstr "предполагаемая дата завершения" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "закрыто" @@ -1668,175 +1685,175 @@ msgstr "параметр '{param}' является обязательным" msgid "'project' parameter is mandatory" msgstr "параметр 'project' является обязательным" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "электронная почта" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "создано" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "идентификатор" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "дополнительный текст к приглашению" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "порядок пользователей" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "Этот пользователем уже является участником проекта" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "очки по умолчанию" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "статусы ПИ по умолчанию" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "статус задачи по умолчанию" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "приоритет по умолчанию" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "важность по умолчанию" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "статус задачи по умолчанию" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "тип задачи по умолчанию" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "участники" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "общее количество вех" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "очки истории" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "активная панель списка задач" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "активная панель kanban" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "активная wiki-панель" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "активная панель задач" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "система видеоконференций" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "дополнительные данные системы видеоконференций" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "шаблон для создания" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "права анонимов" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "права пользователя" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "личное" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "цвета тэгов" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "конфигурация модулей" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "архивировано" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "цвет" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "ограничение на активную работу" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "значение" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "роль владельца по умолчанию" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "параметры по умолчанию" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "статусы ПИ" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "очки" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "статусы задач" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "статусы задач" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "типы задач" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "приоритеты" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "степени важности" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "роли" @@ -1868,20 +1885,20 @@ msgstr "записи истории" msgid "notify users" msgstr "уведомить пользователей" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "пользователь" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "Просмотренные" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "Уведомление существует для данных пользователя и проекта" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "Неверное значение для уровня уведомлений" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2624,51 +2641,51 @@ msgstr "версия" msgid "You can't leave the project if there are no more owners" msgstr "Вы не можете покинуть проект если в нём нет других владельцев" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "Этот почтовый адрес уже используется" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "Неверная роль для этого проекта" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Количество вех должно быть больше или равно нулю" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "Параметры по умолчанию" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "Статусу пользовательских историй" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "Очки" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "Типы задачи" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "Приоритеты" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "Степени важности" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "Роли" @@ -3076,7 +3093,7 @@ msgstr "Критический" #. Translators: User role #: taiga/projects/translations.py:170 msgid "UX" -msgstr "" +msgstr "Юзабилити" #. Translators: User role #: taiga/projects/translations.py:172 @@ -3103,17 +3120,17 @@ msgstr "Владелец продукта" msgid "Stakeholder" msgstr "Заинтересованная сторона" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "" "У вас нет прав чтобы установить спринт для этой пользовательской истории." -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "" "У вас нет прав чтобы установить статус для этой пользовательской истории." -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3196,9 +3213,9 @@ msgstr "последний отредактировавший" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" -msgstr "" +msgstr "Свертесть с историей API для получения изменений" #: taiga/users/admin.py:50 msgid "Personal info" @@ -3212,39 +3229,39 @@ msgstr "Права доступа" msgid "Important dates" msgstr "Важные даты" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "Неверное имя пользователя или e-mail" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "Письмо успешно отправлено!" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" -msgstr "" +msgstr "Неверный токен" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "Поле \"текущий пароль\" является обязательным" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "Поле \"новый пароль\" является обязательным" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "Неверная длина пароля, требуется как минимум 6 символов" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "Неверно указан текущий пароль" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "Список аргументов неполон" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "Неправильный формат изображения" @@ -3265,76 +3282,76 @@ msgstr "Неверно, вы уверены что токен правильны msgid "Invalid, are you sure the token is correct?" msgstr "Неверно, вы уверены что токен правильный?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "статус суперпользователя" -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." -msgstr "" +msgstr "Выбранный пользователь имеет все разрешения, ему не чего назначит." -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "имя пользователя" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "Обязательно. 30 символов или меньше. Буквы, числа и символы /./-/_" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "Введите корректное имя пользователя." -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "активный" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." -msgstr "" +msgstr "Выбранный пользователь активен. Отменить выбор для удаления аккаунта." -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "биография" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "фотография" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "когда присоединился" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "язык по умолчанию" -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "тема по умолчанию" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "временная зона по умолчанию" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "установить цвета для тэгов" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "email токен" -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "новый email адрес" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "разрешения" @@ -3346,7 +3363,7 @@ msgstr "невалидный" msgid "Invalid username. Try with a different one." msgstr "Неверное имя пользователя. Попробуйте другое." -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "Имя пользователя или пароль не соответствуют пользователю." @@ -3477,6 +3494,7 @@ msgstr "Не существует роли с таким идентификат msgid "" "Duplicate key value violates unique constraint. Key '{}' already exists." msgstr "" +"Дублирующий ключ, значение должно быть уникальны. Ключ '{}' уже существует." #: taiga/userstorage/models.py:30 msgid "key" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index f82efa4b..f7604c7a 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-08-28 10:22+0200\n" -"PO-Revision-Date: 2015-08-28 08:23+0000\n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-18 08:52+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" @@ -68,6 +68,7 @@ msgid "Error on creating new user." msgstr "無法創建新使用者" #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 msgid "Invalid token" msgstr "無效的代碼 " @@ -566,9 +567,9 @@ msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" #: taiga/export_import/serializers.py:511 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 -#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 -#: taiga/projects/serializers.py:170 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" @@ -812,11 +813,61 @@ msgstr "" msgid "[%(project)s] Your project dump has been imported" msgstr "[%(project)s] 您堆存的專案已滙入" -#: taiga/feedback/models.py:23 taiga/users/models.py:111 +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "姓名" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "描述" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" msgstr "全名" -#: taiga/feedback/models.py:25 taiga/users/models.py:106 +#: taiga/feedback/models.py:25 taiga/users/models.py:108 msgid "email address" msgstr "電子郵件" @@ -827,7 +878,7 @@ msgstr "評論" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 #: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:131 taiga/projects/models.py:563 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 #: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 #: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 #: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 @@ -896,8 +947,8 @@ msgstr "" msgid "The payload is not a valid json" msgstr "載荷為無效json" -#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:139 -#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:108 +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 msgid "The project doesn't exist" msgstr "專案不存在" @@ -1077,12 +1128,12 @@ msgstr "" "{message}" #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 -#: taiga/permissions/permissions.py:55 +#: taiga/permissions/permissions.py:51 msgid "View project" msgstr "檢視專案" #: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 -#: taiga/permissions/permissions.py:58 +#: taiga/permissions/permissions.py:53 msgid "View milestones" msgstr "檢視里程碑" @@ -1091,22 +1142,22 @@ msgid "View user stories" msgstr "檢視使用者故事" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 -#: taiga/permissions/permissions.py:69 +#: taiga/permissions/permissions.py:63 msgid "View tasks" msgstr "檢視任務 " #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 -#: taiga/permissions/permissions.py:75 +#: taiga/permissions/permissions.py:68 msgid "View issues" msgstr "檢視問題 " #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 -#: taiga/permissions/permissions.py:81 +#: taiga/permissions/permissions.py:73 msgid "View wiki pages" msgstr "檢視維基頁" #: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 -#: taiga/permissions/permissions.py:86 +#: taiga/permissions/permissions.py:78 msgid "View wiki links" msgstr "檢視維基連結" @@ -1134,135 +1185,119 @@ msgstr "加入問題 " msgid "Add comments to issues" msgstr "問題加入評論" -#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:82 +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" msgstr "新增維基頁" -#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:83 +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 msgid "Modify wiki page" msgstr "修改維基頁" -#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:87 +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 msgid "Add wiki link" msgstr "新增維基連結" -#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:88 +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 msgid "Modify wiki link" msgstr "修改維基連結" -#: taiga/permissions/permissions.py:48 taiga/permissions/permissions.py:56 -msgid "Star project" -msgstr "" - -#: taiga/permissions/permissions.py:49 taiga/permissions/permissions.py:67 -msgid "Vote user story" -msgstr "" - -#: taiga/permissions/permissions.py:50 taiga/permissions/permissions.py:73 -msgid "Vote task" -msgstr "" - -#: taiga/permissions/permissions.py:51 taiga/permissions/permissions.py:79 -msgid "Vote issue" -msgstr "" - -#: taiga/permissions/permissions.py:59 +#: taiga/permissions/permissions.py:54 msgid "Add milestone" msgstr "加入里程碑" -#: taiga/permissions/permissions.py:60 +#: taiga/permissions/permissions.py:55 msgid "Modify milestone" msgstr "修改里程碑" -#: taiga/permissions/permissions.py:61 +#: taiga/permissions/permissions.py:56 msgid "Delete milestone" msgstr "刪除里程碑 " -#: taiga/permissions/permissions.py:63 +#: taiga/permissions/permissions.py:58 msgid "View user story" msgstr "檢視使用者故事" -#: taiga/permissions/permissions.py:64 +#: taiga/permissions/permissions.py:59 msgid "Add user story" msgstr "新增使用者故事" -#: taiga/permissions/permissions.py:65 +#: taiga/permissions/permissions.py:60 msgid "Modify user story" msgstr "修改使用者故事" -#: taiga/permissions/permissions.py:66 +#: taiga/permissions/permissions.py:61 msgid "Delete user story" msgstr "刪除使用者故事" -#: taiga/permissions/permissions.py:70 +#: taiga/permissions/permissions.py:64 msgid "Add task" msgstr "新增任務 " -#: taiga/permissions/permissions.py:71 +#: taiga/permissions/permissions.py:65 msgid "Modify task" msgstr "修改任務 " -#: taiga/permissions/permissions.py:72 +#: taiga/permissions/permissions.py:66 msgid "Delete task" msgstr "刪除任務 " -#: taiga/permissions/permissions.py:76 +#: taiga/permissions/permissions.py:69 msgid "Add issue" msgstr "新增問題 " -#: taiga/permissions/permissions.py:77 +#: taiga/permissions/permissions.py:70 msgid "Modify issue" msgstr "修改問題" -#: taiga/permissions/permissions.py:78 +#: taiga/permissions/permissions.py:71 msgid "Delete issue" msgstr "刪除問題 " -#: taiga/permissions/permissions.py:84 +#: taiga/permissions/permissions.py:76 msgid "Delete wiki page" msgstr "刪除維基頁 " -#: taiga/permissions/permissions.py:89 +#: taiga/permissions/permissions.py:81 msgid "Delete wiki link" msgstr "刪除維基連結" -#: taiga/permissions/permissions.py:93 +#: taiga/permissions/permissions.py:85 msgid "Modify project" msgstr "修改專案" -#: taiga/permissions/permissions.py:94 +#: taiga/permissions/permissions.py:86 msgid "Add member" msgstr "新增成員" -#: taiga/permissions/permissions.py:95 +#: taiga/permissions/permissions.py:87 msgid "Remove member" msgstr "移除成員" -#: taiga/permissions/permissions.py:96 +#: taiga/permissions/permissions.py:88 msgid "Delete project" msgstr "刪除專案" -#: taiga/permissions/permissions.py:97 +#: taiga/permissions/permissions.py:89 msgid "Admin project values" msgstr "管理員專案數值" -#: taiga/permissions/permissions.py:98 +#: taiga/permissions/permissions.py:90 msgid "Admin roles" msgstr "管理員角色" -#: taiga/projects/api.py:176 +#: taiga/projects/api.py:203 msgid "Not valid template name" msgstr "非有效樣板名稱 " -#: taiga/projects/api.py:179 +#: taiga/projects/api.py:206 msgid "Not valid template description" msgstr "無效樣板描述" -#: taiga/projects/api.py:455 taiga/projects/serializers.py:264 +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" msgstr "至少需有一位使用者擔任管理員" -#: taiga/projects/api.py:485 +#: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "您無觀看權限" @@ -1275,7 +1310,7 @@ msgid "Project ID not matches between object and project" msgstr "專案ID不符合物件與專案" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 -#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:136 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 @@ -1285,14 +1320,14 @@ msgstr "所有者" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 #: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 -#: taiga/projects/models.py:340 taiga/projects/models.py:366 -#: taiga/projects/models.py:397 taiga/projects/models.py:426 -#: taiga/projects/models.py:459 taiga/projects/models.py:482 -#: taiga/projects/models.py:509 taiga/projects/models.py:540 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:196 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 msgid "project" msgstr "專案" @@ -1307,7 +1342,7 @@ msgstr "物件ID" #: taiga/projects/attachments/models.py:64 #: taiga/projects/custom_attributes/models.py:46 #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 -#: taiga/projects/models.py:134 taiga/projects/models.py:566 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 #: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 msgid "modified date" @@ -1321,22 +1356,13 @@ msgstr "附加檔案" msgid "is deprecated" msgstr "棄用" -#: taiga/projects/attachments/models.py:73 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 -#: taiga/projects/issues/models.py:61 taiga/projects/models.py:129 -#: taiga/projects/models.py:561 taiga/projects/tasks/models.py:60 -#: taiga/projects/userstories/models.py:90 -msgid "description" -msgstr "描述" - #: taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:39 -#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:356 -#: taiga/projects/models.py:393 taiga/projects/models.py:420 -#: taiga/projects/models.py:455 taiga/projects/models.py:478 -#: taiga/projects/models.py:503 taiga/projects/models.py:536 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:191 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 msgid "order" msgstr "次序" @@ -1364,16 +1390,6 @@ msgstr "單行文字" msgid "Multi-Line Text" msgstr "多行列文字" -#: taiga/projects/custom_attributes/models.py:36 -#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:125 -#: taiga/projects/models.py:352 taiga/projects/models.py:391 -#: taiga/projects/models.py:416 taiga/projects/models.py:453 -#: taiga/projects/models.py:476 taiga/projects/models.py:499 -#: taiga/projects/models.py:534 taiga/projects/models.py:557 -#: taiga/users/models.py:183 taiga/webhooks/models.py:27 -msgid "name" -msgstr "姓名" - #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1533,23 +1549,23 @@ msgstr "封鎖筆記" msgid "sprint" msgstr "衝刺任務" -#: taiga/projects/issues/api.py:159 +#: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." msgstr "您無權限設定此問題的衝刺任務" -#: taiga/projects/issues/api.py:163 +#: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." msgstr "您無權限設定此問題的狀態" -#: taiga/projects/issues/api.py:167 +#: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "您無權限設定此問題的嚴重性" -#: taiga/projects/issues/api.py:171 +#: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "您無權限設定此問題的優先性" -#: taiga/projects/issues/api.py:175 +#: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." msgstr "您無權限設定此問題的類型" @@ -1595,10 +1611,10 @@ msgstr "指派給" msgid "external reference" msgstr "外部參考" -#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:127 -#: taiga/projects/models.py:354 taiga/projects/models.py:418 -#: taiga/projects/models.py:501 taiga/projects/models.py:559 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:185 +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 msgid "slug" msgstr "代稱" @@ -1610,8 +1626,8 @@ msgstr "预計開始日期" msgid "estimated finish date" msgstr "預計完成日期" -#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:358 -#: taiga/projects/models.py:422 taiga/projects/models.py:505 +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 msgid "is closed" msgstr "被關閉" @@ -1640,175 +1656,175 @@ msgstr "'{param}' 參數為必要" msgid "'project' parameter is mandatory" msgstr "'project'參數為必要" -#: taiga/projects/models.py:61 +#: taiga/projects/models.py:66 msgid "email" msgstr "電子郵件" -#: taiga/projects/models.py:63 +#: taiga/projects/models.py:68 msgid "create at" msgstr "創建於" -#: taiga/projects/models.py:65 taiga/users/models.py:128 +#: taiga/projects/models.py:70 taiga/users/models.py:130 msgid "token" msgstr "代號" -#: taiga/projects/models.py:71 +#: taiga/projects/models.py:76 msgid "invitation extra text" msgstr "額外文案邀請" -#: taiga/projects/models.py:74 +#: taiga/projects/models.py:79 msgid "user order" msgstr "使用者次序" -#: taiga/projects/models.py:80 +#: taiga/projects/models.py:89 msgid "The user is already member of the project" msgstr "使用者已是專案成員" -#: taiga/projects/models.py:95 +#: taiga/projects/models.py:104 msgid "default points" msgstr "預設點數" -#: taiga/projects/models.py:99 +#: taiga/projects/models.py:108 msgid "default US status" msgstr "預設使用者故事狀態" -#: taiga/projects/models.py:103 +#: taiga/projects/models.py:112 msgid "default task status" msgstr "預設任務狀態" -#: taiga/projects/models.py:106 +#: taiga/projects/models.py:115 msgid "default priority" msgstr "預設優先性" -#: taiga/projects/models.py:109 +#: taiga/projects/models.py:118 msgid "default severity" msgstr "預設嚴重性" -#: taiga/projects/models.py:113 +#: taiga/projects/models.py:122 msgid "default issue status" msgstr "預設問題狀態" -#: taiga/projects/models.py:117 +#: taiga/projects/models.py:126 msgid "default issue type" msgstr "預設議題類型" -#: taiga/projects/models.py:138 +#: taiga/projects/models.py:147 msgid "members" msgstr "成員" -#: taiga/projects/models.py:141 +#: taiga/projects/models.py:150 msgid "total of milestones" msgstr "全部里程碑" -#: taiga/projects/models.py:142 +#: taiga/projects/models.py:151 msgid "total story points" msgstr "全部故事點數" -#: taiga/projects/models.py:145 taiga/projects/models.py:572 +#: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" msgstr "活躍的待辦任務優先表面板" -#: taiga/projects/models.py:147 taiga/projects/models.py:574 +#: taiga/projects/models.py:156 taiga/projects/models.py:616 msgid "active kanban panel" msgstr "活躍的看板式面板" -#: taiga/projects/models.py:149 taiga/projects/models.py:576 +#: taiga/projects/models.py:158 taiga/projects/models.py:618 msgid "active wiki panel" msgstr "活躍的維基面板" -#: taiga/projects/models.py:151 taiga/projects/models.py:578 +#: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" msgstr "活躍的問題面板" -#: taiga/projects/models.py:154 taiga/projects/models.py:581 +#: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" msgstr "視訊會議系統" -#: taiga/projects/models.py:156 taiga/projects/models.py:583 +#: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" msgstr "視訊會議額外資料" -#: taiga/projects/models.py:161 +#: taiga/projects/models.py:170 msgid "creation template" msgstr "創建模版" -#: taiga/projects/models.py:164 +#: taiga/projects/models.py:173 msgid "anonymous permissions" msgstr "匿名權限" -#: taiga/projects/models.py:168 +#: taiga/projects/models.py:177 msgid "user permissions" msgstr "使用者權限" -#: taiga/projects/models.py:171 +#: taiga/projects/models.py:180 msgid "is private" msgstr "私密" -#: taiga/projects/models.py:182 +#: taiga/projects/models.py:191 msgid "tags colors" msgstr "標籤顏色" -#: taiga/projects/models.py:341 +#: taiga/projects/models.py:383 msgid "modules config" msgstr "模組設定" -#: taiga/projects/models.py:360 +#: taiga/projects/models.py:402 msgid "is archived" msgstr "已歸檔" -#: taiga/projects/models.py:362 taiga/projects/models.py:424 -#: taiga/projects/models.py:457 taiga/projects/models.py:480 -#: taiga/projects/models.py:507 taiga/projects/models.py:538 -#: taiga/users/models.py:113 +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 msgid "color" msgstr "顏色" -#: taiga/projects/models.py:364 +#: taiga/projects/models.py:406 msgid "work in progress limit" msgstr "工作進度限制" -#: taiga/projects/models.py:395 taiga/userstorage/models.py:31 +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 msgid "value" msgstr "價值" -#: taiga/projects/models.py:569 +#: taiga/projects/models.py:611 msgid "default owner's role" msgstr "預設所有者角色" -#: taiga/projects/models.py:585 +#: taiga/projects/models.py:627 msgid "default options" msgstr "預設選項" -#: taiga/projects/models.py:586 +#: taiga/projects/models.py:628 msgid "us statuses" msgstr "我們狀況" -#: taiga/projects/models.py:587 taiga/projects/userstories/models.py:40 +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 #: taiga/projects/userstories/models.py:72 msgid "points" msgstr "點數" -#: taiga/projects/models.py:588 +#: taiga/projects/models.py:630 msgid "task statuses" msgstr "任務狀況" -#: taiga/projects/models.py:589 +#: taiga/projects/models.py:631 msgid "issue statuses" msgstr "問題狀況" -#: taiga/projects/models.py:590 +#: taiga/projects/models.py:632 msgid "issue types" msgstr "問題類型" -#: taiga/projects/models.py:591 +#: taiga/projects/models.py:633 msgid "priorities" msgstr "優先性" -#: taiga/projects/models.py:592 +#: taiga/projects/models.py:634 msgid "severities" msgstr "嚴重性" -#: taiga/projects/models.py:593 +#: taiga/projects/models.py:635 msgid "roles" msgstr "角色" @@ -1840,20 +1856,20 @@ msgstr "歷史輸入" msgid "notify users" msgstr "通知用戶" -#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 -msgid "user" -msgstr "" - #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:64 -#: taiga/projects/notifications/services.py:78 +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" msgstr "通知特定使用者與專案退出" +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "" + #: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 #, python-format msgid "" @@ -2599,51 +2615,51 @@ msgstr "版本" msgid "You can't leave the project if there are no more owners" msgstr "如果專案無所有者,你將無法脫離該專案" -#: taiga/projects/serializers.py:240 +#: taiga/projects/serializers.py:242 msgid "Email address is already taken" msgstr "電子郵件已使用" -#: taiga/projects/serializers.py:252 +#: taiga/projects/serializers.py:254 msgid "Invalid role for the project" msgstr "專案無效的角色" -#: taiga/projects/serializers.py:346 +#: taiga/projects/serializers.py:352 msgid "Total milestones must be major or equal to zero" msgstr "Kanban" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:409 msgid "Default options" msgstr "預設選項" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:410 msgid "User story's statuses" msgstr "使用者故事狀態" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:411 msgid "Points" msgstr "點數" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:412 msgid "Task's statuses" msgstr "任務狀態" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:413 msgid "Issue's statuses" msgstr "問題狀態" -#: taiga/projects/serializers.py:408 +#: taiga/projects/serializers.py:414 msgid "Issue's types" msgstr "問題類型" -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:415 msgid "Priorities" msgstr "優先性" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:416 msgid "Severities" msgstr "嚴重性" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:417 msgid "Roles" msgstr "角色" @@ -3065,15 +3081,15 @@ msgstr "產品所有人" msgid "Stakeholder" msgstr "利害關係人" -#: taiga/projects/userstories/api.py:155 +#: taiga/projects/userstories/api.py:156 msgid "You don't have permissions to set this sprint to this user story." msgstr "無權限更動使用者故事的衝刺任務" -#: taiga/projects/userstories/api.py:159 +#: taiga/projects/userstories/api.py:160 msgid "You don't have permissions to set this status to this user story." msgstr "無權限更動此使用者故事的狀態" -#: taiga/projects/userstories/api.py:259 +#: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "" "Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " @@ -3155,7 +3171,7 @@ msgstr "上次更改" msgid "href" msgstr "href" -#: taiga/timeline/signals.py:86 +#: taiga/timeline/signals.py:67 msgid "Check the history API for the exact diff" msgstr "" @@ -3171,39 +3187,39 @@ msgstr "許可" msgid "Important dates" msgstr "重要日期" -#: taiga/users/api.py:151 taiga/users/api.py:158 +#: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" msgstr "無效使用者或郵件" -#: taiga/users/api.py:167 +#: taiga/users/api.py:166 msgid "Mail sended successful!" msgstr "成功送出郵件" -#: taiga/users/api.py:179 taiga/users/api.py:184 +#: taiga/users/api.py:178 taiga/users/api.py:183 msgid "Token is invalid" msgstr "代號無效" -#: taiga/users/api.py:205 +#: taiga/users/api.py:204 msgid "Current password parameter needed" msgstr "需要目前密碼之參數" -#: taiga/users/api.py:208 +#: taiga/users/api.py:207 msgid "New password parameter needed" msgstr "需要新密碼參數" -#: taiga/users/api.py:211 +#: taiga/users/api.py:210 msgid "Invalid password length at least 6 charaters needed" msgstr "無效密碼長度,至少需6個字元" -#: taiga/users/api.py:214 +#: taiga/users/api.py:213 msgid "Invalid current password" msgstr "無效密碼" -#: taiga/users/api.py:230 +#: taiga/users/api.py:229 msgid "Incomplete arguments" msgstr "不完整參數" -#: taiga/users/api.py:235 +#: taiga/users/api.py:234 msgid "Invalid image format" msgstr "無效的圖片檔案" @@ -3224,76 +3240,76 @@ msgstr "無效,請確認代號正確,之前是否曾使用過?" msgid "Invalid, are you sure the token is correct?" msgstr "無效,請確認代號是否正確?" -#: taiga/users/models.py:69 +#: taiga/users/models.py:71 msgid "superuser status" msgstr "超級使用者狀態 " -#: taiga/users/models.py:70 +#: taiga/users/models.py:72 msgid "" "Designates that this user has all permissions without explicitly assigning " "them." msgstr "無經明確分派,即賦予該使用者所有權限," -#: taiga/users/models.py:100 +#: taiga/users/models.py:102 msgid "username" msgstr "使用者名稱" -#: taiga/users/models.py:101 +#: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "必填。最多30字元(可為數字,字母,符號....)" -#: taiga/users/models.py:104 +#: taiga/users/models.py:106 msgid "Enter a valid username." msgstr "輸入有效的使用者名稱 " -#: taiga/users/models.py:107 +#: taiga/users/models.py:109 msgid "active" msgstr "活躍" -#: taiga/users/models.py:108 +#: taiga/users/models.py:110 msgid "" "Designates whether this user should be treated as active. Unselect this " "instead of deleting accounts." msgstr "賦予該使用者活躍角色,以不選擇取代刪除帳戶功能。" -#: taiga/users/models.py:114 +#: taiga/users/models.py:116 msgid "biography" msgstr "自傳" -#: taiga/users/models.py:117 +#: taiga/users/models.py:119 msgid "photo" msgstr "照片" -#: taiga/users/models.py:118 +#: taiga/users/models.py:120 msgid "date joined" msgstr "加入日期" -#: taiga/users/models.py:120 +#: taiga/users/models.py:122 msgid "default language" msgstr "預設語言 " -#: taiga/users/models.py:122 +#: taiga/users/models.py:124 msgid "default theme" msgstr "預設主題" -#: taiga/users/models.py:124 +#: taiga/users/models.py:126 msgid "default timezone" msgstr "預設時區" -#: taiga/users/models.py:126 +#: taiga/users/models.py:128 msgid "colorize tags" msgstr "顏色標籤" -#: taiga/users/models.py:131 +#: taiga/users/models.py:133 msgid "email token" msgstr "電子郵件符號 " -#: taiga/users/models.py:133 +#: taiga/users/models.py:135 msgid "new email address" msgstr "新電子郵件地址" -#: taiga/users/models.py:188 +#: taiga/users/models.py:203 msgid "permissions" msgstr "許可" @@ -3305,7 +3321,7 @@ msgstr "無效" msgid "Invalid username. Try with a different one." msgstr "無效使用者名稱,請重試其它名稱 " -#: taiga/users/services.py:49 taiga/users/services.py:53 +#: taiga/users/services.py:52 taiga/users/services.py:56 msgid "Username or password does not matches user." msgstr "用戶名稱與密碼不符" From e7078fcb7ddc7044f7d47de79abb0464ef3237e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 21 Sep 2015 17:32:32 +0200 Subject: [PATCH 136/190] Fix a typo --- taiga/projects/attachments/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/attachments/api.py b/taiga/projects/attachments/api.py index 6018433a..a369a5cb 100644 --- a/taiga/projects/attachments/api.py +++ b/taiga/projects/attachments/api.py @@ -44,7 +44,7 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCru def update(self, *args, **kwargs): partial = kwargs.get("partial", False) if not partial: - raise exc.NotSupported(_("Non partial updates not supported")) + raise exc.NotSupported(_("Partial updates are not supported")) return super().update(*args, **kwargs) def get_content_type(self): From 1fd480ff77953a94f109719285922e0f8ad07c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 22 Sep 2015 11:03:37 +0200 Subject: [PATCH 137/190] [i18n] Add italiano (it) --- CHANGELOG.md | 1 + settings/common.py | 2 +- taiga/locale/it/LC_MESSAGES/django.po | 3882 +++++++++++++++++++++++++ 3 files changed, 3884 insertions(+), 1 deletion(-) create mode 100644 taiga/locale/it/LC_MESSAGES/django.po diff --git a/CHANGELOG.md b/CHANGELOG.md index ed0e5c9f..442e75c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. - Add russian (ru) translation. + - Add italian (it) translation. ### Misc diff --git a/settings/common.py b/settings/common.py index 644fb501..9a256a45 100644 --- a/settings/common.py +++ b/settings/common.py @@ -105,7 +105,7 @@ LANGUAGES = [ #("id", "Bahasa Indonesia"), # Indonesian #("io", "IDO"), # Ido #("is", "Íslenska"), # Icelandic - #("it", "Italiano"), # Italian + ("it", "Italiano"), # Italian #("ja", "日本語"), # Japanese #("ka", "ქართული"), # Georgian #("kk", "Қазақша"), # Kazakh diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po new file mode 100644 index 00000000..ddd9b220 --- /dev/null +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -0,0 +1,3882 @@ +# taiga-back.taiga. +# Copyright (C) 2015 Taiga Dev Team +# This file is distributed under the same license as the taiga-back package. +# +# Translators: +# Andrea Raimondi , 2015 +# luca corsato , 2015 +# Marco Somma , 2015 +# Marco Vito Moscaritolo , 2015 +# Vittorio Della Rossa , 2015 +msgid "" +msgstr "" +"Project-Id-Version: taiga-back\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"PO-Revision-Date: 2015-09-20 19:29+0000\n" +"Last-Translator: Andrea Raimondi \n" +"Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" +"language/it/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: taiga/auth/api.py:99 +msgid "Public register is disabled." +msgstr "Registro pubblico disabilitato" + +#: taiga/auth/api.py:132 +msgid "invalid register type" +msgstr "Tipo di registro invalido" + +#: taiga/auth/api.py:145 +msgid "invalid login type" +msgstr "Tipo di login invalido" + +#: taiga/auth/serializers.py:34 taiga/users/serializers.py:61 +msgid "invalid username" +msgstr "Username non valido" + +#: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 +msgid "" +"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" +msgstr "" +"Sono richiesti 255 caratteri, o meno: lettere, numeri e caratteri /./-/_ " + +#: taiga/auth/services.py:75 +msgid "Username is already in use." +msgstr "Il nome utente scelto è già in uso" + +#: taiga/auth/services.py:78 +msgid "Email is already in use." +msgstr "L'email inserita è già in uso" + +#: taiga/auth/services.py:94 +msgid "Token not matches any valid invitation." +msgstr "Il token non corrisponde a nessun invito valido" + +#: taiga/auth/services.py:122 +msgid "User is already registered." +msgstr "L'Utente è già registrato." + +#: taiga/auth/services.py:146 +msgid "Membership with user is already exists." +msgstr "L'associazione con l'utente è già avvenuta." + +#: taiga/auth/services.py:172 +msgid "Error on creating new user." +msgstr "Errore nella creazione dell'utente." + +#: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 +#: taiga/external_apps/services.py:34 +msgid "Invalid token" +msgstr "Token non valido" + +#: taiga/base/api/fields.py:268 +msgid "This field is required." +msgstr "Questo campo è obbligatorio." + +#: taiga/base/api/fields.py:269 taiga/base/api/relations.py:311 +msgid "Invalid value." +msgstr "Valore non valido." + +#: taiga/base/api/fields.py:453 +#, python-format +msgid "'%s' value must be either True or False." +msgstr "il valore di '%s' deve essere o vero o falso." + +#: taiga/base/api/fields.py:517 +msgid "" +"Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." +msgstr "" +"Inserisci uno slug valido composto da lettere, numeri, caratteri di " +"sottolineatura o trattini" + +#: taiga/base/api/fields.py:532 +#, python-format +msgid "Select a valid choice. %(value)s is not one of the available choices." +msgstr "Seleziona un valore valido. %(value)s non è una scelta disponibile." + +#: taiga/base/api/fields.py:595 +msgid "Enter a valid email address." +msgstr "Inserisci un indirizzo e-mail valido." + +#: taiga/base/api/fields.py:637 +#, python-format +msgid "Date has wrong format. Use one of these formats instead: %s" +msgstr "La data non ha un formato valido. Usa uno dei formati disponibili: %s" + +#: taiga/base/api/fields.py:701 +#, python-format +msgid "Datetime has wrong format. Use one of these formats instead: %s" +msgstr "L'orario non ha un formato valido. Usa uno dei formati disponibili: %s" + +#: taiga/base/api/fields.py:771 +#, python-format +msgid "Time has wrong format. Use one of these formats instead: %s" +msgstr "Formato temporale errato. Usare uno dei seguenti formati: %s" + +#: taiga/base/api/fields.py:828 +msgid "Enter a whole number." +msgstr "Inserire il numero completo." + +#: taiga/base/api/fields.py:829 taiga/base/api/fields.py:882 +#, python-format +msgid "Ensure this value is less than or equal to %(limit_value)s." +msgstr "Assicurati che il valore sia minore o uguale a %(limit_value)s." + +#: taiga/base/api/fields.py:830 taiga/base/api/fields.py:883 +#, python-format +msgid "Ensure this value is greater than or equal to %(limit_value)s." +msgstr "Assicurati che il valore sia maggiore o uguale a %(limit_value)s." + +#: taiga/base/api/fields.py:860 +#, python-format +msgid "\"%s\" value must be a float." +msgstr "il valore \"%s\" deve essere un valore \"float\"." + +#: taiga/base/api/fields.py:881 +msgid "Enter a number." +msgstr "Inserisci un numero" + +#: taiga/base/api/fields.py:884 +#, python-format +msgid "Ensure that there are no more than %s digits in total." +msgstr "Assicurati che non ci siano più di %s cifre in totale." + +#: taiga/base/api/fields.py:885 +#, python-format +msgid "Ensure that there are no more than %s decimal places." +msgstr "Assicurati che non ci siano più di %s decimali." + +#: taiga/base/api/fields.py:886 +#, python-format +msgid "Ensure that there are no more than %s digits before the decimal point." +msgstr "Assicurati che non ci siano più di %s cifre prima del punto decimale." + +#: taiga/base/api/fields.py:953 +msgid "No file was submitted. Check the encoding type on the form." +msgstr "" +"Non è stato caricato nessun file. Controlla il tipo di codifica nella scheda." + +#: taiga/base/api/fields.py:954 +msgid "No file was submitted." +msgstr "Nessun file è stato caricato." + +#: taiga/base/api/fields.py:955 +msgid "The submitted file is empty." +msgstr "Il file caricato è vuoto." + +#: taiga/base/api/fields.py:956 +#, python-format +msgid "" +"Ensure this filename has at most %(max)d characters (it has %(length)d)." +msgstr "" +"Assicurati che il nome del file abbiamo al massimo %(max)d caratteri (ne ha " +"%(length)d)." + +#: taiga/base/api/fields.py:957 +msgid "Please either submit a file or check the clear checkbox, not both." +msgstr "" +"Per favore carica il file oppure controlla la casella deselezionata, non " +"entrambi. " + +#: taiga/base/api/fields.py:997 +msgid "" +"Upload a valid image. The file you uploaded was either not an image or a " +"corrupted image." +msgstr "" +"Carica un'immagina valida. Il file caricato potrebbe non essere un'immagine " +"o l'immagine potrebbe essere corrotta. " + +#: taiga/base/api/pagination.py:115 +msgid "Page is not 'last', nor can it be converted to an int." +msgstr "La pagina non è 'last', né può essere convertita come int." + +#: taiga/base/api/pagination.py:119 +#, python-format +msgid "Invalid page (%(page_number)s): %(message)s" +msgstr "Pagina (%(page_number)s) invalida: %(message)s" + +#: taiga/base/api/permissions.py:61 +msgid "Invalid permission definition." +msgstr "Definizione invalida di permesso. " + +#: taiga/base/api/relations.py:221 +#, python-format +msgid "Invalid pk '%s' - object does not exist." +msgstr "pk '%s' invalido - l'oggetto non esiste" + +#: taiga/base/api/relations.py:222 +#, python-format +msgid "Incorrect type. Expected pk value, received %s." +msgstr "Inserimento scorretto. Atteso un valore pk, ricevuto %s." + +#: taiga/base/api/relations.py:310 +#, python-format +msgid "Object with %s=%s does not exist." +msgstr "L'oggetto con %s=%s non esiste." + +#: taiga/base/api/relations.py:346 +msgid "Invalid hyperlink - No URL match" +msgstr "Hyperlink invalido - nessun URL abbinato" + +#: taiga/base/api/relations.py:347 +msgid "Invalid hyperlink - Incorrect URL match" +msgstr "Hyperlink invalido - l'URL abbinato non è corretto" + +#: taiga/base/api/relations.py:348 +msgid "Invalid hyperlink due to configuration error" +msgstr "URL invalido a causa di un errore di configurazione" + +#: taiga/base/api/relations.py:349 +msgid "Invalid hyperlink - object does not exist." +msgstr "Hyperlink invalido - l'oggetto non esiste" + +#: taiga/base/api/relations.py:350 +#, python-format +msgid "Incorrect type. Expected url string, received %s." +msgstr "Inserimento scorretto. Attesa una stringa con URL, ricevuto %s." + +#: taiga/base/api/serializers.py:296 +msgid "Invalid data" +msgstr "Dati non validi" + +#: taiga/base/api/serializers.py:388 +msgid "No input provided" +msgstr "Nessun inserimento fornito" + +#: taiga/base/api/serializers.py:548 +msgid "Cannot create a new item, only existing items may be updated." +msgstr "" +"Non è possibile creare un nuovo elemento, solo quelli esistenti possono " +"essere aggiornati" + +#: taiga/base/api/serializers.py:559 +msgid "Expected a list of items." +msgstr "Ci si aspetta una lista di oggetti." + +#: taiga/base/api/views.py:100 +msgid "Not found" +msgstr "Non trovato" + +#: taiga/base/api/views.py:103 +msgid "Permission denied" +msgstr "Permesso negato" + +#: taiga/base/api/views.py:451 +msgid "Server application error" +msgstr "Errore sul server" + +#: taiga/base/connectors/exceptions.py:24 +msgid "Connection error." +msgstr "Errore di connessione" + +#: taiga/base/exceptions.py:53 +msgid "Malformed request." +msgstr "Richiesta composta erroneamente." + +#: taiga/base/exceptions.py:58 +msgid "Incorrect authentication credentials." +msgstr "Le credenziali non sono corrette" + +#: taiga/base/exceptions.py:63 +msgid "Authentication credentials were not provided." +msgstr "Le credenziali per l'autenticazione non sono state fornite." + +#: taiga/base/exceptions.py:68 +msgid "You do not have permission to perform this action." +msgstr "Non hai il permesso per eseguire l'azione. " + +#: taiga/base/exceptions.py:73 +#, python-format +msgid "Method '%s' not allowed." +msgstr "Metodo '%s' non permesso." + +#: taiga/base/exceptions.py:81 +msgid "Could not satisfy the request's Accept header" +msgstr "" +"Non è possibile soddisfare la richiesta di accettazione dell'intestazione." + +#: taiga/base/exceptions.py:90 +#, python-format +msgid "Unsupported media type '%s' in request." +msgstr "Nella richiesta è presente un contenuto media '%s' non supportato." + +#: taiga/base/exceptions.py:98 +msgid "Request was throttled." +msgstr "La richiesta è stata soppressa" + +#: taiga/base/exceptions.py:99 +#, python-format +msgid "Expected available in %d second%s." +msgstr "Disponibile in %d secondi%s." + +#: taiga/base/exceptions.py:113 +msgid "Unexpected error" +msgstr "Errore inaspettato" + +#: taiga/base/exceptions.py:125 +msgid "Not found." +msgstr "Assente." + +#: taiga/base/exceptions.py:130 +msgid "Method not supported for this endpoint." +msgstr "Metodo non supportato dall'endpoint." + +#: taiga/base/exceptions.py:138 taiga/base/exceptions.py:146 +msgid "Wrong arguments." +msgstr "Argomento errato." + +#: taiga/base/exceptions.py:150 +msgid "Data validation error" +msgstr "Errore di validazione dei dati" + +#: taiga/base/exceptions.py:162 +msgid "Integrity Error for wrong or invalid arguments" +msgstr "Errore di integrità causato da un argomento invalido o sbagliato" + +#: taiga/base/exceptions.py:169 +msgid "Precondition error" +msgstr "Errore di precondizione" + +#: taiga/base/filters.py:80 +msgid "Error in filter params types." +msgstr "Errore nel filtro del tipo di parametri." + +#: taiga/base/filters.py:134 taiga/base/filters.py:223 +#: taiga/base/filters.py:272 +msgid "'project' must be an integer value." +msgstr "'Progetto' deve essere un valore intero." + +#: taiga/base/tags.py:25 +msgid "tags" +msgstr "tag" + +#: taiga/base/templates/emails/base-body-html.jinja:6 +msgid "Taiga" +msgstr "Taiga" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Follow us on Twitter" +msgstr "Seguici su Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:406 +#: taiga/base/templates/emails/hero-body-html.jinja:380 +#: taiga/base/templates/emails/updates-body-html.jinja:442 +msgid "Twitter" +msgstr "Twitter" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "Get the code on GitHub" +msgstr "Prendi il codice su GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:407 +#: taiga/base/templates/emails/hero-body-html.jinja:381 +#: taiga/base/templates/emails/updates-body-html.jinja:443 +msgid "GitHub" +msgstr "GitHub" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Visit our website" +msgstr "Visita il nostro sito" + +#: taiga/base/templates/emails/base-body-html.jinja:408 +#: taiga/base/templates/emails/hero-body-html.jinja:382 +#: taiga/base/templates/emails/updates-body-html.jinja:444 +msgid "Taiga.io" +msgstr "Taiga.io" + +#: taiga/base/templates/emails/base-body-html.jinja:423 +#: taiga/base/templates/emails/hero-body-html.jinja:397 +#: taiga/base/templates/emails/updates-body-html.jinja:459 +#, python-format +msgid "" +"\n" +" Taiga Support:\n" +" %(support_url)s\n" +"
\n" +" Contact us:\n" +" \n" +" %(support_email)s\n" +" \n" +"
\n" +" Mailing list:\n" +" \n" +" %(mailing_list_url)s\n" +" \n" +" " +msgstr "" +"\n" +"Supporto Taiga:\n" +"\n" +"" +"%(support_url)s\n" +"\n" +"
\n" +"\n" +"Contact us:\n" +"\n" +"\n" +"\n" +"%(support_email)s\n" +"\n" +"\n" +"\n" +"
\n" +"\n" +"Mailing list:\n" +"\n" +"\n" +"\n" +"%(mailing_list_url)s\n" +"\n" +"" + +#: taiga/base/templates/emails/hero-body-html.jinja:6 +msgid "You have been Taigatized" +msgstr "Sei stato Taigatizzato" + +#: taiga/base/templates/emails/hero-body-html.jinja:359 +msgid "" +"\n" +"

You have been Taigatized!" +"

\n" +"

Welcome to Taiga, an Open " +"Source, Agile Project Management Tool

\n" +" " +msgstr "" +"\n" +"

Sei stato Taigato!

\n" +"\n" +"

Benvenuto in Taiga, uno strumento open source per la gestione agile dei " +"progetti

" + +#: taiga/base/templates/emails/updates-body-html.jinja:6 +msgid "[Taiga] Updates" +msgstr "[Taiga] aggiornamento" + +#: taiga/base/templates/emails/updates-body-html.jinja:417 +msgid "Updates" +msgstr "Aggiornamenti" + +#: taiga/base/templates/emails/updates-body-html.jinja:423 +#, python-format +msgid "" +"\n" +"

comment:" +"

\n" +"

" +"%(comment)s

\n" +" " +msgstr "" +"\n" +"

commento:

\n" +"\n" +"

%(comment)s

" + +#: taiga/base/templates/emails/updates-body-text.jinja:6 +#, python-format +msgid "" +"\n" +" Comment: %(comment)s\n" +" " +msgstr "" +"\n" +"Commento: %(comment)s" + +#: taiga/export_import/api.py:103 +msgid "We needed at least one role" +msgstr "Abbiamo bisogno di almeno un ruolo" + +#: taiga/export_import/api.py:197 +msgid "Needed dump file" +msgstr "E' richiesto un file di dump" + +#: taiga/export_import/api.py:204 +msgid "Invalid dump format" +msgstr "Formato di dump invalido" + +#: taiga/export_import/dump_service.py:96 +msgid "error importing project data" +msgstr "Errore nell'importazione del progetto dati" + +#: taiga/export_import/dump_service.py:109 +msgid "error importing lists of project attributes" +msgstr "Errore nell'importazione della lista degli attributi di progetto" + +#: taiga/export_import/dump_service.py:114 +msgid "error importing default project attributes values" +msgstr "" +"Errore nell'importazione dei valori degli attributi del progetto predefinito" + +#: taiga/export_import/dump_service.py:124 +msgid "error importing custom attributes" +msgstr "Errore nell'importazione degli attributi personalizzati" + +#: taiga/export_import/dump_service.py:129 +msgid "error importing roles" +msgstr "Errore nell'importazione i ruoli" + +#: taiga/export_import/dump_service.py:144 +msgid "error importing memberships" +msgstr "Errore nell'importazione delle iscrizioni" + +#: taiga/export_import/dump_service.py:149 +msgid "error importing sprints" +msgstr "errore nell'importazione degli sprints" + +#: taiga/export_import/dump_service.py:154 +msgid "error importing wiki pages" +msgstr "Errore nell'importazione delle pagine wiki" + +#: taiga/export_import/dump_service.py:159 +msgid "error importing wiki links" +msgstr "Errore nell'importazione dei link di wiki" + +#: taiga/export_import/dump_service.py:164 +msgid "error importing issues" +msgstr "errore nell'importazione dei problemi" + +#: taiga/export_import/dump_service.py:169 +msgid "error importing user stories" +msgstr "Errore nell'importazione delle user story" + +#: taiga/export_import/dump_service.py:174 +msgid "error importing tasks" +msgstr "Errore nell'importazione dei compiti " + +#: taiga/export_import/dump_service.py:179 +msgid "error importing tags" +msgstr "Errore nell'importazione dei tags" + +#: taiga/export_import/dump_service.py:183 +msgid "error importing timelines" +msgstr "Errore nell'importazione delle timelines" + +#: taiga/export_import/serializers.py:163 +msgid "{}=\"{}\" not found in this project" +msgstr "{}=\"{}\" non è stato trovato in questo progetto" + +#: taiga/export_import/serializers.py:428 +#: taiga/projects/custom_attributes/serializers.py:103 +msgid "Invalid content. It must be {\"key\": \"value\",...}" +msgstr "Contenuto errato. Deve essere {\"key\": \"value\",...}" + +#: taiga/export_import/serializers.py:443 +#: taiga/projects/custom_attributes/serializers.py:118 +msgid "It contain invalid custom fields." +msgstr "Contiene campi personalizzati invalidi." + +#: taiga/export_import/serializers.py:511 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 +#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 +#: taiga/projects/serializers.py:172 +msgid "Name duplicated for the project" +msgstr "Il nome del progetto è duplicato" + +#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +msgid "Error generating project dump" +msgstr "Errore nella creazione del dump di progetto" + +#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +msgid "Error loading project dump" +msgstr "Errore nel caricamento del dump di progetto" + +#: taiga/export_import/templates/emails/dump_project-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump generated

\n" +"

Hello %(user)s,

\n" +"

Your dump from project %(project)s has been correctly generated.\n" +"

You can download it here:

\n" +" Download the dump file\n" +"

This file will be deleted on %(deletion_date)s.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

E' stato generato il dump di progetto

\n" +"\n" +"

Salve %(user)s,

\n" +"\n" +"

Il dump del tuo progetto %(project)s è stato creato correttamente

\n" +"\n" +"

Puoi scaricarlo qui:

\n" +"\n" +"Scarica l file\n" +"\n" +"

Questa file sarà cancellato il %(deletion_date)s.

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/export_import/templates/emails/dump_project-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your dump from project %(project)s has been correctly generated. You can " +"download it here:\n" +"\n" +"%(url)s\n" +"\n" +"This file will be deleted on %(deletion_date)s.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"Salve %(user)s,\n" +"\n" +"\n" +"\n" +"Il dump del tuo progetto %(project)s e' stato creato correttamente. Puoi " +"scaricarlo qui:\n" +"\n" +"\n" +"\n" +"%(url)s\n" +"\n" +"\n" +"\n" +"Questo file verrà cancellato il %(deletion_date)s.\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/export_import/templates/emails/dump_project-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been generated" +msgstr "[%(project)s] Il dump del tuo progetto è stato creato correttamente" + +#: taiga/export_import/templates/emails/export_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project %(project)s has not been exported correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

%(error_message)s

\n" +"\n" +"

Salve %(user)s,

\n" +"\n" +"

Il tuo progetto %(project)s non è stato esportato correttamente.

\n" +"\n" +"

Gli amministratori di sistema di Taiga sono stati informati.
Per " +"favore, fai du nuovo un tentativo o contatta il team di supporto a:\n" +"\n" +"%(support_email)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/export_import/templates/emails/export_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"Your project %(project)s has not been exported correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Salve %(user)s,\n" +"\n" +"\n" +"\n" +"%(error_message)s\n" +"\n" +"Il tuo progetto %(project)s non è stato esportato correttamente.\n" +"\n" +"\n" +"\n" +"Gli amministratori di sistema di Taiga sono stati informati.\n" +"\n" +"\n" +"\n" +"Per favore, fai du nuovo un tentativo o contatta il team di supporto a: " +"%(support_email)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/export_import/templates/emails/export_error-subject.jinja:1 +#, python-format +msgid "[%(project)s] %(error_subject)s" +msgstr "[%(project)s] %(error_subject)s" + +#: taiga/export_import/templates/emails/import_error-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

%(error_message)s

\n" +"

Hello %(user)s,

\n" +"

Your project has not been importer correctly.

\n" +"

The Taiga system administrators have been informed.
Please, try " +"it again or contact with the support team at\n" +" %(support_email)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"

%(error_message)s

\n" +"\n" +"

Salve %(user)s,

\n" +"\n" +"

Il tuo progetto non è stato importato correttamente

\n" +"\n" +"

Gli amministratori di sistema di Taiga sono stati informati.
Per " +"favore, prova di nuovo o contatta il tema di supporto a:\n" +"\n" +"%(support_email)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/export_import/templates/emails/import_error-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"%(error_message)s\n" +"\n" +"Your project has not been importer correctly.\n" +"\n" +"The Taiga system administrators have been informed.\n" +"\n" +"Please, try it again or contact with the support team at %(support_email)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Salve %(user)s,\n" +"\n" +"\n" +"\n" +"%(error_message)s\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"Gli amministratori di sistema di Taiga sono stati informati\n" +"\n" +"\n" +"\n" +"Per favore riprova di nuovo o contatta il nostro team di supporto a " +"%(support_email)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/export_import/templates/emails/import_error-subject.jinja:1 +#, python-format +msgid "[Taiga] %(error_subject)s" +msgstr "[Taiga] %(error_subject)s" + +#: taiga/export_import/templates/emails/load_dump-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Project dump imported

\n" +"

Hello %(user)s,

\n" +"

Your project dump has been correctly imported.

\n" +" Go to %(project)s\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Il dump di progetto è stato importato

\n" +"\n" +"

Salve%(user)s,

\n" +"\n" +"

Il dump del tuo progetto è stato importato correttamente.

\n" +"\n" +"Vai a %(project)s\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/export_import/templates/emails/load_dump-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(user)s,\n" +"\n" +"Your project dump has been correctly imported.\n" +"\n" +"You can see the project %(project)s here:\n" +"\n" +"%(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Salve %(user)s,\n" +"\n" +"\n" +"\n" +"Il dump del tuo progetto è stato importato correttamente\n" +"\n" +"\n" +"\n" +"Puoi vedere il progetto %(project)s qui:\n" +"\n" +"\n" +"\n" +"%(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/export_import/templates/emails/load_dump-subject.jinja:1 +#, python-format +msgid "[%(project)s] Your project dump has been imported" +msgstr "[%(project)s] Il dump del tuo progetto è stato importato" + +#: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 +#: taiga/external_apps/api.py:73 +msgid "Authentication required" +msgstr "E' richiesta l'autenticazione" + +#: taiga/external_apps/models.py:33 +#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 +#: taiga/projects/models.py:394 taiga/projects/models.py:433 +#: taiga/projects/models.py:458 taiga/projects/models.py:495 +#: taiga/projects/models.py:518 taiga/projects/models.py:541 +#: taiga/projects/models.py:576 taiga/projects/models.py:599 +#: taiga/users/models.py:198 taiga/webhooks/models.py:27 +msgid "name" +msgstr "nome" + +#: taiga/external_apps/models.py:35 +msgid "Icon url" +msgstr "Url dell'icona" + +#: taiga/external_apps/models.py:36 +msgid "web" +msgstr "web" + +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/projects/custom_attributes/models.py:37 +#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 +#: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 +#: taiga/projects/userstories/models.py:90 +msgid "description" +msgstr "descrizione" + +#: taiga/external_apps/models.py:39 +msgid "Next url" +msgstr "Url successivo" + +#: taiga/external_apps/models.py:41 +msgid "secret key for cyphering the application tokens" +msgstr "chiave segreta per cifrare i token dell'applicazione" + +#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 +#: taiga/projects/votes/models.py:50 +msgid "user" +msgstr "utente" + +#: taiga/external_apps/models.py:59 +msgid "application" +msgstr "applicazione" + +#: taiga/feedback/models.py:23 taiga/users/models.py:113 +msgid "full name" +msgstr "Nome completo" + +#: taiga/feedback/models.py:25 taiga/users/models.py:108 +msgid "email address" +msgstr "Inserisci un indirizzo e-mail valido." + +#: taiga/feedback/models.py:27 +msgid "comment" +msgstr "Commento" + +#: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 +#: taiga/projects/custom_attributes/models.py:44 +#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 +#: taiga/projects/models.py:140 taiga/projects/models.py:605 +#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 +#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 +#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +msgid "created date" +msgstr "data creata" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Feedback

\n" +"

Taiga has received feedback from %(full_name)s <%(email)s>

\n" +" " +msgstr "" +"\n" +"

Feedback

\n" +"

Taiga ha ricevuto un feedback da %(full_name)s <%(email)s>

" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:9 +#, python-format +msgid "" +"\n" +"

Comment

\n" +"

%(comment)s

\n" +" " +msgstr "" +"\n" +"

Commento

\n" +"\n" +"

%(comment)s

" + +#: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 +#: taiga/users/admin.py:51 +msgid "Extra info" +msgstr "Informazioni extra" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:1 +#, python-format +msgid "" +"---------\n" +"- From: %(full_name)s <%(email)s>\n" +"---------\n" +"- Comment:\n" +"%(comment)s\n" +"---------" +msgstr "" +"---------\n" +"\n" +"- Da: %(full_name)s <%(email)s>\n" +"\n" +"---------\n" +"\n" +"- Commento:\n" +"\n" +"%(comment)s\n" +"\n" +"---------" + +#: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:8 +msgid "- Extra info:" +msgstr "- Maggiori informazioni:" + +#: taiga/feedback/templates/emails/feedback_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Feedback from %(full_name)s <%(email)s>\n" +msgstr "" +"\n" +"\n" +"[Taiga] Hai un feedback da %(full_name)s <%(email)s>\n" + +#: taiga/hooks/api.py:52 +msgid "The payload is not a valid json" +msgstr "Il carico non è un son valido" + +#: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 +#: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 +msgid "The project doesn't exist" +msgstr "Il progetto non esiste" + +#: taiga/hooks/api.py:64 +msgid "Bad signature" +msgstr "Firma non valida" + +#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/gitlab/event_hooks.py:73 +msgid "The referenced element doesn't exist" +msgstr "L'elemento di riferimento non esiste" + +#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/gitlab/event_hooks.py:80 +msgid "The status doesn't exist" +msgstr "Lo stato non esiste" + +#: taiga/hooks/bitbucket/event_hooks.py:86 +msgid "Status changed from BitBucket commit" +msgstr "Lo stato è stato modificato a seguito di un commit di BitBucket" + +#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 +msgid "Invalid issue information" +msgstr "Informazione sul problema non valida" + +#: taiga/hooks/bitbucket/event_hooks.py:131 +#, python-brace-format +msgid "" +"Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Problema creato da [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") da BitBucket.\n" +"\n" +"Origine del problema su BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Go to 'bb#{number} - {subject}'\"):\n" +"\n" +"\n" +"\n" +"{description}" + +#: taiga/hooks/bitbucket/event_hooks.py:142 +msgid "Issue created from BitBucket." +msgstr "Problema creato da BItBucket" + +#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 +#: taiga/hooks/gitlab/event_hooks.py:152 +msgid "Invalid issue comment information" +msgstr "Commento sul problema non valido" + +#: taiga/hooks/bitbucket/event_hooks.py:174 +#, python-brace-format +msgid "" +"Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") from BitBucket.\n" +"Origin BitBucket issue: [bb#{number} - {subject}]({bitbucket_url} \"Go to " +"'bb#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Commento da [@{bitbucket_user_name}]({bitbucket_user_url} \"See " +"@{bitbucket_user_name}'s BitBucket profile\") da BitBucket.\n" +"\n" +"Origine del problema da BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"\"Go to 'bb#{number} - {subject}'\")\n" +"\n" +"\n" +"\n" +"{message}" + +#: taiga/hooks/bitbucket/event_hooks.py:185 +#, python-brace-format +msgid "" +"Comment From BitBucket:\n" +"\n" +"{message}" +msgstr "" +"Commento da BitBucket:\n" +"\n" +"\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:96 +#, python-brace-format +msgid "" +"Status changed by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." +msgstr "" +"Stato cambiato da [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub commit [{commit_id}]" +"({commit_url} \"See commit '{commit_id} - {commit_message}'\")." + +#: taiga/hooks/github/event_hooks.py:107 +msgid "Status changed from GitHub commit." +msgstr "Lo stato è stato modificato da un commit su GitHub." + +#: taiga/hooks/github/event_hooks.py:157 +#, python-brace-format +msgid "" +"Issue created by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\"):\n" +"\n" +"{description}" +msgstr "" +"Problema creato da [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") su GitHub.\n" +"\n" +"Origine del problema su GitHub: [gh#{number} - {subject}]({github_url} \"Go " +"to 'gh#{number} - {subject}'\"):\n" +"\n" +"\n" +"\n" +"{description}" + +#: taiga/hooks/github/event_hooks.py:168 +msgid "Issue created from GitHub." +msgstr "Problema creato da GitHub" + +#: taiga/hooks/github/event_hooks.py:200 +#, python-brace-format +msgid "" +"Comment by [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") from GitHub.\n" +"Origin GitHub issue: [gh#{number} - {subject}]({github_url} \"Go to " +"'gh#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Commento da [@{github_user_name}]({github_user_url} \"See " +"@{github_user_name}'s GitHub profile\") su GitHub.\n" +"Origine del problema su GitHub: [gh#{number} - {subject}]({github_url} \"Go " +"to 'gh#{number} - {subject}'\")\n" +"\n" +"{message}" + +#: taiga/hooks/github/event_hooks.py:211 +#, python-brace-format +msgid "" +"Comment From GitHub:\n" +"\n" +"{message}" +msgstr "" +"Commento su GitHub:\n" +"\n" +"\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:86 +msgid "Status changed from GitLab commit" +msgstr "Lo stato è stato modificato da un commit su GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:128 +msgid "Created from GitLab" +msgstr "Creato da GitLab" + +#: taiga/hooks/gitlab/event_hooks.py:160 +#, python-brace-format +msgid "" +"Comment by [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") from GitLab.\n" +"Origin GitLab issue: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"'gl#{number} - {subject}'\")\n" +"\n" +"{message}" +msgstr "" +"Commento da [@{gitlab_user_name}]({gitlab_user_url} \"See " +"@{gitlab_user_name}'s GitLab profile\") su GitLab.\n" +"\n" +"Origine del problema su GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go " +"to 'gl#{number} - {subject}'\")\n" +"\n" +"\n" +"\n" +"\n" +"{message}" + +#: taiga/hooks/gitlab/event_hooks.py:171 +#, python-brace-format +msgid "" +"Comment From GitLab:\n" +"\n" +"{message}" +msgstr "" +"Commento da GitLab:\n" +"\n" +"\n" +"\n" +"{message}" + +#: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 +#: taiga/permissions/permissions.py:51 +msgid "View project" +msgstr "Vedi progetto" + +#: taiga/permissions/permissions.py:22 taiga/permissions/permissions.py:32 +#: taiga/permissions/permissions.py:53 +msgid "View milestones" +msgstr "Guarda le milestones" + +#: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 +msgid "View user stories" +msgstr "Guarda le user story" + +#: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 +#: taiga/permissions/permissions.py:63 +msgid "View tasks" +msgstr "Guarda i compiti" + +#: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 +#: taiga/permissions/permissions.py:68 +msgid "View issues" +msgstr "Guarda i problemi" + +#: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 +#: taiga/permissions/permissions.py:73 +msgid "View wiki pages" +msgstr "Guarda le pagine wiki" + +#: taiga/permissions/permissions.py:27 taiga/permissions/permissions.py:37 +#: taiga/permissions/permissions.py:78 +msgid "View wiki links" +msgstr "Guarda i lik di wiki" + +#: taiga/permissions/permissions.py:38 +msgid "Request membership" +msgstr "Richiedi l'iscrizione" + +#: taiga/permissions/permissions.py:39 +msgid "Add user story to project" +msgstr "Aggiungi una user story al progetto" + +#: taiga/permissions/permissions.py:40 +msgid "Add comments to user stories" +msgstr "Aggiungi dei commenti alle user story" + +#: taiga/permissions/permissions.py:41 +msgid "Add comments to tasks" +msgstr "Aggiungi i commenti ai compiti" + +#: taiga/permissions/permissions.py:42 +msgid "Add issues" +msgstr "Aggiungi i problemi" + +#: taiga/permissions/permissions.py:43 +msgid "Add comments to issues" +msgstr "Aggiungi commenti ai problemi" + +#: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 +msgid "Add wiki page" +msgstr "Aggiungi una pagina wiki" + +#: taiga/permissions/permissions.py:45 taiga/permissions/permissions.py:75 +msgid "Modify wiki page" +msgstr "Modifica la pagina wiki" + +#: taiga/permissions/permissions.py:46 taiga/permissions/permissions.py:79 +msgid "Add wiki link" +msgstr "Aggiungi un link wiki" + +#: taiga/permissions/permissions.py:47 taiga/permissions/permissions.py:80 +msgid "Modify wiki link" +msgstr "Modifica il link di wiki" + +#: taiga/permissions/permissions.py:54 +msgid "Add milestone" +msgstr "Aggiungi una tappa" + +#: taiga/permissions/permissions.py:55 +msgid "Modify milestone" +msgstr "Modifica la tappa" + +#: taiga/permissions/permissions.py:56 +msgid "Delete milestone" +msgstr "Elimina la tappa" + +#: taiga/permissions/permissions.py:58 +msgid "View user story" +msgstr "Guarda la user story" + +#: taiga/permissions/permissions.py:59 +msgid "Add user story" +msgstr "Aggiungi una user story" + +#: taiga/permissions/permissions.py:60 +msgid "Modify user story" +msgstr "Modifica una user story" + +#: taiga/permissions/permissions.py:61 +msgid "Delete user story" +msgstr "Cancella una user story" + +#: taiga/permissions/permissions.py:64 +msgid "Add task" +msgstr "Aggiungi un compito" + +#: taiga/permissions/permissions.py:65 +msgid "Modify task" +msgstr "Modifica il compito" + +#: taiga/permissions/permissions.py:66 +msgid "Delete task" +msgstr "Elimina compito" + +#: taiga/permissions/permissions.py:69 +msgid "Add issue" +msgstr "Aggiungi un problema" + +#: taiga/permissions/permissions.py:70 +msgid "Modify issue" +msgstr "Modifica il problema" + +#: taiga/permissions/permissions.py:71 +msgid "Delete issue" +msgstr "Elimina il problema" + +#: taiga/permissions/permissions.py:76 +msgid "Delete wiki page" +msgstr "Elimina la pagina wiki" + +#: taiga/permissions/permissions.py:81 +msgid "Delete wiki link" +msgstr "Elimina la pagina wiki" + +#: taiga/permissions/permissions.py:85 +msgid "Modify project" +msgstr "Modifica il progetto" + +#: taiga/permissions/permissions.py:86 +msgid "Add member" +msgstr "Aggiungi un membro" + +#: taiga/permissions/permissions.py:87 +msgid "Remove member" +msgstr "Rimuovo il membro" + +#: taiga/permissions/permissions.py:88 +msgid "Delete project" +msgstr "Elimina il progetto" + +#: taiga/permissions/permissions.py:89 +msgid "Admin project values" +msgstr "Valori dell'amministratore del progetto" + +#: taiga/permissions/permissions.py:90 +msgid "Admin roles" +msgstr "Ruoli dell'amministratore" + +#: taiga/projects/api.py:203 +msgid "Not valid template name" +msgstr "Il nome del template non è valido" + +#: taiga/projects/api.py:206 +msgid "Not valid template description" +msgstr "La descrizione del template non è valida" + +#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +msgid "At least one of the user must be an active admin" +msgstr "Almeno uno degli utenti deve essere un amministratore attivo" + +#: taiga/projects/api.py:512 +msgid "You don't have permisions to see that." +msgstr "Non hai il permesso di vedere questo elemento." + +#: taiga/projects/attachments/api.py:47 +msgid "Non partial updates not supported" +msgstr "Aggiornamento non parziale non supportato" + +#: taiga/projects/attachments/api.py:62 +msgid "Project ID not matches between object and project" +msgstr "L'ID di progetto non corrisponde tra oggetto e progetto" + +#: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 +#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 +#: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/userstorage/models.py:25 +msgid "owner" +msgstr "proprietario" + +#: taiga/projects/attachments/models.py:54 +#: taiga/projects/custom_attributes/models.py:41 +#: taiga/projects/issues/models.py:51 taiga/projects/milestones/models.py:41 +#: taiga/projects/models.py:382 taiga/projects/models.py:408 +#: taiga/projects/models.py:439 taiga/projects/models.py:468 +#: taiga/projects/models.py:501 taiga/projects/models.py:524 +#: taiga/projects/models.py:551 taiga/projects/models.py:582 +#: taiga/projects/notifications/models.py:71 +#: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 +#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +msgid "project" +msgstr "progetto" + +#: taiga/projects/attachments/models.py:56 +msgid "content type" +msgstr "tipo di contenuto" + +#: taiga/projects/attachments/models.py:58 +msgid "object id" +msgstr "ID dell'oggetto" + +#: taiga/projects/attachments/models.py:64 +#: taiga/projects/custom_attributes/models.py:46 +#: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 +#: taiga/projects/models.py:143 taiga/projects/models.py:608 +#: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 +#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +msgid "modified date" +msgstr "data modificata" + +#: taiga/projects/attachments/models.py:69 +msgid "attached file" +msgstr "file allegato" + +#: taiga/projects/attachments/models.py:72 +msgid "is deprecated" +msgstr "non approvato" + +#: taiga/projects/attachments/models.py:74 +#: taiga/projects/custom_attributes/models.py:39 +#: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 +#: taiga/projects/models.py:435 taiga/projects/models.py:462 +#: taiga/projects/models.py:497 taiga/projects/models.py:520 +#: taiga/projects/models.py:545 taiga/projects/models.py:578 +#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +msgid "order" +msgstr "ordine" + +#: taiga/projects/choices.py:21 +msgid "AppearIn" +msgstr "ApparIn" + +#: taiga/projects/choices.py:22 +msgid "Jitsi" +msgstr "Jitsi" + +#: taiga/projects/choices.py:23 +msgid "Custom" +msgstr "Personalizzato" + +#: taiga/projects/choices.py:24 +msgid "Talky" +msgstr "Talky" + +#: taiga/projects/custom_attributes/models.py:33 +msgid "Text" +msgstr "Testo" + +#: taiga/projects/custom_attributes/models.py:34 +msgid "Multi-Line Text" +msgstr "Testo multi-linea" + +#: taiga/projects/custom_attributes/models.py:38 +#: taiga/projects/issues/models.py:46 +msgid "type" +msgstr "tipo" + +#: taiga/projects/custom_attributes/models.py:87 +msgid "values" +msgstr "valori" + +#: taiga/projects/custom_attributes/models.py:97 +#: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 +msgid "user story" +msgstr "user story" + +#: taiga/projects/custom_attributes/models.py:112 +msgid "task" +msgstr "compito" + +#: taiga/projects/custom_attributes/models.py:127 +msgid "issue" +msgstr "problema" + +#: taiga/projects/custom_attributes/serializers.py:57 +msgid "Already exists one with the same name." +msgstr "Ne esiste già uno con lo stesso nome" + +#: taiga/projects/history/api.py:70 +msgid "Comment already deleted" +msgstr "Il commento è già stato eliminato" + +#: taiga/projects/history/api.py:89 +msgid "Comment not deleted" +msgstr "Commento non eliminato" + +#: taiga/projects/history/choices.py:27 +msgid "Change" +msgstr "Cambiato" + +#: taiga/projects/history/choices.py:28 +msgid "Create" +msgstr "Creato" + +#: taiga/projects/history/choices.py:29 +msgid "Delete" +msgstr "Eliminato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:22 +#, python-format +msgid "%(role)s role points" +msgstr "%(role)s punti del ruolo" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:25 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:130 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:133 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:156 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:193 +msgid "from" +msgstr "da" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:31 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:141 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:144 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:162 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:179 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:199 +msgid "to" +msgstr "a" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:43 +msgid "Added new attachment" +msgstr "Aggiunto un nuovo allegato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:61 +msgid "Updated attachment" +msgstr "Allegato aggiornato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:67 +msgid "deprecated" +msgstr "non approvato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:69 +msgid "not deprecated" +msgstr "accettato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:85 +msgid "Deleted attachment" +msgstr "Allegato eliminato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:104 +msgid "added" +msgstr "aggiunto" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:109 +msgid "removed" +msgstr "rimosso" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 +#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +msgid "Unassigned" +msgstr "Non assegnato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:211 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:86 +msgid "-deleted-" +msgstr "-eliminato-" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "to:" +msgstr "a:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:20 +msgid "from:" +msgstr "da:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:26 +msgid "Added" +msgstr "Aggiunto" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:33 +msgid "Changed" +msgstr "Modificato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:40 +msgid "Deleted" +msgstr "Eliminato" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:54 +msgid "added:" +msgstr "aggiunto:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:57 +msgid "removed:" +msgstr "rimosso:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:62 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:79 +msgid "From:" +msgstr "Da:" + +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:63 +#: taiga/projects/history/templates/emails/includes/fields_diff-text.jinja:80 +msgid "To:" +msgstr "A:" + +#: taiga/projects/history/templatetags/functions.py:26 +#: taiga/projects/wiki/models.py:32 +msgid "content" +msgstr "contenuto" + +#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/mixins/blocked.py:31 +msgid "blocked note" +msgstr "nota bloccata" + +#: taiga/projects/history/templatetags/functions.py:28 +msgid "sprint" +msgstr "sprint" + +#: taiga/projects/issues/api.py:160 +msgid "You don't have permissions to set this sprint to this issue." +msgstr "Non hai i permessi per aggiungere questo sprint a questo problema" + +#: taiga/projects/issues/api.py:164 +msgid "You don't have permissions to set this status to this issue." +msgstr "Non hai i permessi per aggiungere questo stato a questo problema" + +#: taiga/projects/issues/api.py:168 +msgid "You don't have permissions to set this severity to this issue." +msgstr "Non hai i permessi per aggiungere questa criticità a questo problema" + +#: taiga/projects/issues/api.py:172 +msgid "You don't have permissions to set this priority to this issue." +msgstr "Non hai i permessi per aggiungere questa priorità a questo problema." + +#: taiga/projects/issues/api.py:176 +msgid "You don't have permissions to set this type to this issue." +msgstr "Non hai i permessi per aggiungere questa tipologia a questo problema" + +#: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 +#: taiga/projects/userstories/models.py:57 +msgid "ref" +msgstr "referenza" + +#: taiga/projects/issues/models.py:40 taiga/projects/tasks/models.py:39 +#: taiga/projects/userstories/models.py:67 +msgid "status" +msgstr "stato" + +#: taiga/projects/issues/models.py:42 +msgid "severity" +msgstr "criticità" + +#: taiga/projects/issues/models.py:44 +msgid "priority" +msgstr "priorità" + +#: taiga/projects/issues/models.py:49 taiga/projects/tasks/models.py:44 +#: taiga/projects/userstories/models.py:60 +msgid "milestone" +msgstr "tappa" + +#: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 +msgid "finished date" +msgstr "data di conclusione" + +#: taiga/projects/issues/models.py:60 taiga/projects/tasks/models.py:53 +#: taiga/projects/userstories/models.py:89 +msgid "subject" +msgstr "soggeto" + +#: taiga/projects/issues/models.py:64 taiga/projects/tasks/models.py:63 +#: taiga/projects/userstories/models.py:93 +msgid "assigned to" +msgstr "assegnato a" + +#: taiga/projects/issues/models.py:66 taiga/projects/tasks/models.py:67 +#: taiga/projects/userstories/models.py:103 +msgid "external reference" +msgstr "referenza esterna" + +#: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 +#: taiga/projects/models.py:396 taiga/projects/models.py:460 +#: taiga/projects/models.py:543 taiga/projects/models.py:601 +#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +msgid "slug" +msgstr "lumaca" + +#: taiga/projects/milestones/models.py:42 +msgid "estimated start date" +msgstr "data stimata di inizio" + +#: taiga/projects/milestones/models.py:43 +msgid "estimated finish date" +msgstr "data stimata di fine" + +#: taiga/projects/milestones/models.py:50 taiga/projects/models.py:400 +#: taiga/projects/models.py:464 taiga/projects/models.py:547 +msgid "is closed" +msgstr "è concluso" + +#: taiga/projects/milestones/models.py:52 +msgid "disponibility" +msgstr "disponibilità" + +#: taiga/projects/milestones/models.py:75 +msgid "The estimated start must be previous to the estimated finish." +msgstr "" +"La data stimata di inizio deve essere precedente alla data stimata di fine." + +#: taiga/projects/milestones/validators.py:12 +msgid "There's no sprint with that id" +msgstr "Non c'è nessuno sprint on questo ID" + +#: taiga/projects/mixins/blocked.py:29 +msgid "is blocked" +msgstr "è bloccato" + +#: taiga/projects/mixins/ordering.py:47 +#, python-brace-format +msgid "'{param}' parameter is mandatory" +msgstr "il parametro '{param}' è obbligatorio" + +#: taiga/projects/mixins/ordering.py:51 +msgid "'project' parameter is mandatory" +msgstr "il parametro 'project' è obbligatorio" + +#: taiga/projects/models.py:66 +msgid "email" +msgstr "email" + +#: taiga/projects/models.py:68 +msgid "create at" +msgstr "creato a " + +#: taiga/projects/models.py:70 taiga/users/models.py:130 +msgid "token" +msgstr "token" + +#: taiga/projects/models.py:76 +msgid "invitation extra text" +msgstr "testo ulteriore per l'invito" + +#: taiga/projects/models.py:79 +msgid "user order" +msgstr "ordine dell'utente" + +#: taiga/projects/models.py:89 +msgid "The user is already member of the project" +msgstr "L'utente è già membro del progetto" + +#: taiga/projects/models.py:104 +msgid "default points" +msgstr "punti predefiniti" + +#: taiga/projects/models.py:108 +msgid "default US status" +msgstr "stati predefiniti per le storie utente" + +#: taiga/projects/models.py:112 +msgid "default task status" +msgstr "stati predefiniti del compito" + +#: taiga/projects/models.py:115 +msgid "default priority" +msgstr "priorità predefinita" + +#: taiga/projects/models.py:118 +msgid "default severity" +msgstr "criticità predefinita" + +#: taiga/projects/models.py:122 +msgid "default issue status" +msgstr "stato predefinito del problema" + +#: taiga/projects/models.py:126 +msgid "default issue type" +msgstr "tipologia predefinita del problema" + +#: taiga/projects/models.py:147 +msgid "members" +msgstr "membri" + +#: taiga/projects/models.py:150 +msgid "total of milestones" +msgstr "tappe totali" + +#: taiga/projects/models.py:151 +msgid "total story points" +msgstr "punti totali della storia" + +#: taiga/projects/models.py:154 taiga/projects/models.py:614 +msgid "active backlog panel" +msgstr "pannello di backlog attivo" + +#: taiga/projects/models.py:156 taiga/projects/models.py:616 +msgid "active kanban panel" +msgstr "pannello kanban attivo" + +#: taiga/projects/models.py:158 taiga/projects/models.py:618 +msgid "active wiki panel" +msgstr "pannello wiki attivo" + +#: taiga/projects/models.py:160 taiga/projects/models.py:620 +msgid "active issues panel" +msgstr "pannello dei problemi attivo" + +#: taiga/projects/models.py:163 taiga/projects/models.py:623 +msgid "videoconference system" +msgstr "sistema di videoconferenza" + +#: taiga/projects/models.py:165 taiga/projects/models.py:625 +msgid "videoconference extra data" +msgstr "ulteriori dati di videoconferenza " + +#: taiga/projects/models.py:170 +msgid "creation template" +msgstr "creazione del template" + +#: taiga/projects/models.py:173 +msgid "anonymous permissions" +msgstr "permessi anonimi" + +#: taiga/projects/models.py:177 +msgid "user permissions" +msgstr "permessi dell'utente" + +#: taiga/projects/models.py:180 +msgid "is private" +msgstr "è privato" + +#: taiga/projects/models.py:191 +msgid "tags colors" +msgstr "colori dei tag" + +#: taiga/projects/models.py:383 +msgid "modules config" +msgstr "configurazione dei moduli" + +#: taiga/projects/models.py:402 +msgid "is archived" +msgstr "è archivitato" + +#: taiga/projects/models.py:404 taiga/projects/models.py:466 +#: taiga/projects/models.py:499 taiga/projects/models.py:522 +#: taiga/projects/models.py:549 taiga/projects/models.py:580 +#: taiga/users/models.py:115 +msgid "color" +msgstr "colore" + +#: taiga/projects/models.py:406 +msgid "work in progress limit" +msgstr "limite dei lavori in corso" + +#: taiga/projects/models.py:437 taiga/userstorage/models.py:31 +msgid "value" +msgstr "valore" + +#: taiga/projects/models.py:611 +msgid "default owner's role" +msgstr "ruolo proprietario predefinito" + +#: taiga/projects/models.py:627 +msgid "default options" +msgstr "opzioni predefinite " + +#: taiga/projects/models.py:628 +msgid "us statuses" +msgstr "stati della storia utente" + +#: taiga/projects/models.py:629 taiga/projects/userstories/models.py:40 +#: taiga/projects/userstories/models.py:72 +msgid "points" +msgstr "punti" + +#: taiga/projects/models.py:630 +msgid "task statuses" +msgstr "stati del compito" + +#: taiga/projects/models.py:631 +msgid "issue statuses" +msgstr "stati del probema" + +#: taiga/projects/models.py:632 +msgid "issue types" +msgstr "tipologie del problema" + +#: taiga/projects/models.py:633 +msgid "priorities" +msgstr "priorità" + +#: taiga/projects/models.py:634 +msgid "severities" +msgstr "criticità " + +#: taiga/projects/models.py:635 +msgid "roles" +msgstr "ruoli" + +#: taiga/projects/notifications/choices.py:28 +msgid "Not watching" +msgstr "Non in osservazione" + +#: taiga/projects/notifications/choices.py:29 +msgid "Watching" +msgstr "In osservazione" + +#: taiga/projects/notifications/choices.py:30 +msgid "Ignoring" +msgstr "Ignorato" + +#: taiga/projects/notifications/models.py:61 +msgid "created date time" +msgstr "tempo e data creati" + +#: taiga/projects/notifications/models.py:63 +msgid "updated date time" +msgstr "tempo e data aggiornati" + +#: taiga/projects/notifications/models.py:65 +msgid "history entries" +msgstr "inserimenti della storia" + +#: taiga/projects/notifications/models.py:68 +msgid "notify users" +msgstr "notifica utenti" + +#: taiga/projects/notifications/models.py:90 +#: taiga/projects/notifications/models.py:91 +msgid "Watched" +msgstr "Osservato" + +#: taiga/projects/notifications/services.py:66 +#: taiga/projects/notifications/services.py:80 +msgid "Notify exists for specified user and project" +msgstr "La notifica esiste per l'utente e il progetto specificato " + +#: taiga/projects/notifications/services.py:428 +msgid "Invalid value for notify level" +msgstr "Valore non valido per il livello di notifica" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue updated

\n" +"

Hello %(user)s,
%(changer)s has updated an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +" " +msgstr "" +"\n" +"\n" +"

Problema aggiornato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha aggiornato un problema su " +"%(project)s

\n" +"\n" +"

Problema #%(ref)s %(subject)s

\n" +"\n" +"See issue" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Issue updated\n" +"Hello %(user)s, %(changer)s has updated an issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"\n" +"Problema aggiornato\n" +"\n" +"Salve %(user)s, %(changer)s ha aggiornato un problema su %(project)s\n" +"\n" +"Vai al problema #%(ref)s %(subject)s at %(url)s\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha aggiornato il problema #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New issue created

\n" +"

Hello %(user)s,
%(changer)s has created a new issue on " +"%(project)s

\n" +"

Issue #%(ref)s %(subject)s

\n" +" See issue\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

E' stato creato un nuovo problema

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha creato un nuovo problema su " +"%(project)s

\n" +"\n" +"

Problema #%(ref)s %(subject)s

\n" +"\n" +"Vai al problema\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New issue created\n" +"Hello %(user)s, %(changer)s has created a new issue on %(project)s\n" +"See issue #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"E' stato creato un nuovo problema\n" +"\n" +"Salve %(user)s, %(changer)s ha creato un nuovo problema %(project)s\n" +"\n" +"Vai al problema #%(ref)s %(subject)s su %(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha creato il problema #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Issue deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an issue on %(project)s\n" +"

Issue #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Problema eliminato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha eliminato un problema %(project)s\n" +"\n" +"

Issue #%(ref)s %(subject)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Issue deleted\n" +"Hello %(user)s, %(changer)s has deleted an issue on %(project)s\n" +"Issue #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Problema eliminato\n" +"\n" +"Salve %(user)s, %(changer)s ha eliminato un problema su %(project)s\n" +"\n" +"Problema #%(ref)s %(subject)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" +"\n" + +#: taiga/projects/notifications/templates/emails/issues/issue-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha eliminato il problema #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint updated

\n" +"

Hello %(user)s,
%(changer)s has updated an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See sprint\n" +" " +msgstr "" +"\n" +"\n" +"

Sprint aggiornato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha aggiornato uno sprint su %(project)s\n" +"\n" +"

Sprint %(name)s

\n" +"\n" +"Vedi lo sprint" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Sprint updated\n" +"Hello %(user)s, %(changer)s has updated a sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +msgstr "" +"\n" +"\n" +"Sprint updated\n" +"\n" +"Salve %(user)s, %(changer)s ha aggiornato uno sprint su %(project)s\n" +"\n" +"Guarda lo sprint %(name)s a %(url)s\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha aggiornato lo sprint \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New sprint created

\n" +"

Hello %(user)s,
%(changer)s has created a new sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +" See " +"sprint\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

E' stato creato un nuovo sprint

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha creato un nuovo sprint su " +"%(project)s

\n" +"\n" +"

Sprint %(name)s

\n" +"\n" +"See " +"sprint\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New sprint created\n" +"Hello %(user)s, %(changer)s has created a new sprint on %(project)s\n" +"See sprint %(name)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"E' stato creato un nuovo sprint\n" +"\n" +"Salve %(user)s, %(changer)s ha creato un nuovo sprint su %(project)s\n" +"\n" +"Guarda lo sprint %(name)s su %(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha creato lo sprint \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Sprint deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted an sprint on " +"%(project)s

\n" +"

Sprint %(name)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Sprint eliminato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha eliminato uno sprint di %(project)s\n" +"\n" +"

Sprint %(name)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Sprint deleted\n" +"Hello %(user)s, %(changer)s has deleted an sprint on %(project)s\n" +"Sprint %(name)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Sprint eliminato\n" +"\n" +"Salve %(user)s, %(changer)s ha eliminato uno sprint di %(project)s\n" +"\n" +"Sprint %(name)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/milestones/milestone-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha eliminato uno sprint \"%(milestone)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task updated

\n" +"

Hello %(user)s,
%(changer)s has updated a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +" " +msgstr "" +"\n" +"\n" +"

Compito aggiornato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha aggiornato un compito di %(project)s\n" +"\n" +"

Compito #%(ref)s %(subject)s

\n" +"\n" +"Guarda il compito" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Task updated\n" +"Hello %(user)s, %(changer)s has updated a task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"\n" +"Compito aggiornato\n" +"\n" +"Salve %(user)s, %(changer)s ha aggiornato un compito di %(project)s\n" +"\n" +"Guarda un compito #%(ref)s %(subject)s a %(url)s\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha aggiornato un compito #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New task created

\n" +"

Hello %(user)s,
%(changer)s has created a new task on " +"%(project)s

\n" +"

Task #%(ref)s %(subject)s

\n" +" See task\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

E' stato creato un nuovo compito

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha creato un nuovo compito su " +"%(project)s

\n" +"\n" +"

Compito #%(ref)s %(subject)s

\n" +"\n" +"See task\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New task created\n" +"Hello %(user)s, %(changer)s has created a new task on %(project)s\n" +"See task #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"E' stato creato un nuovo compito\n" +"\n" +"Salve %(user)s, %(changer)s ha creato un nuovo compito su %(project)s\n" +"\n" +"Guarda il compito #%(ref)s %(subject)s a %(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha creato il compito #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Task deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a task on %(project)s\n" +"

Task #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Compito eliminato

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha eliminato un compito su %(project)s\n" +"\n" +"

Task #%(ref)s %(subject)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Task deleted\n" +"Hello %(user)s, %(changer)s has deleted a task on %(project)s\n" +"Task #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Compito eliminato\n" +"\n" +"Hello %(user)s, %(changer)s ha eliminato un compito di %(project)s\n" +"\n" +"Compito #%(ref)s %(subject)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/tasks/task-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha eliminato il compito #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story updated

\n" +"

Hello %(user)s,
%(changer)s has updated a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +" " +msgstr "" +"\n" +"

User Story aggiornata

\n" +"

Ciao %(user)s,
%(changer)s ha aggiornato una user story in " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"Guarda la User Story" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"User story updated\n" +"Hello %(user)s, %(changer)s has updated a user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +msgstr "" +"\n" +"User story aggiornata\n" +"Ciao %(user)s, %(changer)s ha aggiornato una user story in %(project)s\n" +"Guarda user story #%(ref)s %(subject)s in %(url)s\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha aggiornato la storia utente #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New user story created

\n" +"

Hello %(user)s,
%(changer)s has created a new user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +" See user story\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

E' stata creata una nuovo storia utente

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha creato una nuovo storia utente su " +"%(project)s

\n" +"\n" +"

User Story #%(ref)s %(subject)s

\n" +"\n" +"See user story\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New user story created\n" +"Hello %(user)s, %(changer)s has created a new user story on %(project)s\n" +"See user story #%(ref)s %(subject)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"E' stata creata una nuova storia utente\n" +"\n" +"Salve %(user)s, %(changer)s ha creato una nuova storia utente su " +"%(project)s\n" +"\n" +"Guarda la storia utente #%(ref)s %(subject)s su %(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha creato la storia utente #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

User Story deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a user story on " +"%(project)s

\n" +"

User Story #%(ref)s %(subject)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Storia utente eliminata

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha eliminato una storia utente su " +"%(project)s

\n" +"\n" +"

User Story #%(ref)s %(subject)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"User Story deleted\n" +"Hello %(user)s, %(changer)s has deleted a user story on %(project)s\n" +"User Story #%(ref)s %(subject)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Storia utente eliminata\n" +"\n" +"Salve %(user)s, %(changer)s ha eliminato una storia utente di %(project)s\n" +"\n" +"Storia utente #%(ref)s %(subject)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/userstories/userstory-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha eliminato la storia utente #%(ref)s \"%(subject)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki Page updated

\n" +"

Hello %(user)s,
%(changer)s has updated a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See Wiki Page\n" +" " +msgstr "" +"\n" +"\n" +"

Pagina wiki aggiornata

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha aggiornato una pagina wiki su " +"%(project)s

\n" +"\n" +"

Wiki page %(page)s

\n" +"\n" +"Guarda " +"la pagina wiki" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-text.jinja:3 +#, python-format +msgid "" +"\n" +"Wiki Page updated\n" +"\n" +"Hello %(user)s, %(changer)s has updated a wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +msgstr "" +"\n" +"\n" +"Pagina wiki aggiornata\n" +"\n" +"\n" +"\n" +"Salve %(user)s, %(changer)s ha aggiornato una pagina wiki su %(project)s\n" +"\n" +"\n" +"\n" +"Guarda la pagina wiki %(page)s a %(url)s\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Updated the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha aggiornato la pagina wiki \"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

New wiki page created

\n" +"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +" See " +"wiki page\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

E' stata creata una nuova pagina wiki

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha creato una nuova pagina wiki su " +"%(project)s

\n" +"\n" +"

Wiki page %(page)s

\n" +"\n" +"Guarda la " +"pagina wiki\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"New wiki page created\n" +"\n" +"Hello %(user)s, %(changer)s has created a new wiki page on %(project)s\n" +"\n" +"See wiki page %(page)s at %(url)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"E' stata creata una nuova pagina wiki\n" +"\n" +"\n" +"\n" +"Salve %(user)s, %(changer)s ha creato una nuova pagina wiki su %(project)s\n" +"\n" +"\n" +"\n" +"Guarda la pagina wiki %(page)s a %(url)s\n" +"\n" +"\n" +"\n" +"---\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-create-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Created the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha creato la pagina wiki \"%(page)s\"\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Wiki page deleted

\n" +"

Hello %(user)s,
%(changer)s has deleted a wiki page on " +"%(project)s

\n" +"

Wiki page %(page)s

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Pagina wiki eliminata

\n" +"\n" +"

Salve %(user)s,
%(changer)s ha eliminato una pagina wiki su " +"%(project)s

\n" +"\n" +"

Wiki page %(page)s

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Wiki page deleted\n" +"\n" +"Hello %(user)s, %(changer)s has deleted a wiki page on %(project)s\n" +"\n" +"Wiki page %(page)s\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Pagina wiki eliminata\n" +"\n" +"\n" +"\n" +"Salve %(user)s, %(changer)s ha eliminato una pagina wiki su %(project)s\n" +"\n" +"\n" +"\n" +"Pagina wiki %(page)s\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/notifications/templates/emails/wiki/wikipage-delete-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" +msgstr "" +"\n" +"\n" +"[%(project)s] ha eliminato la pagina wiki \"%(page)s\"\n" + +#: taiga/projects/notifications/validators.py:45 +msgid "Watchers contains invalid users" +msgstr "L'osservatore contiene un utente non valido" + +#: taiga/projects/occ/mixins.py:35 +msgid "The version must be an integer" +msgstr "La versione deve essere un intero" + +#: taiga/projects/occ/mixins.py:58 +msgid "The version parameter is not valid" +msgstr "Il parametro della versione non è valido" + +#: taiga/projects/occ/mixins.py:74 +msgid "The version doesn't match with the current one" +msgstr "La versione non corrisponde a quella corrente" + +#: taiga/projects/occ/mixins.py:93 +msgid "version" +msgstr "versione" + +#: taiga/projects/permissions.py:39 +msgid "You can't leave the project if there are no more owners" +msgstr "Non puoi abbandonare il progetto se non ci sono più proprietari" + +#: taiga/projects/serializers.py:242 +msgid "Email address is already taken" +msgstr "L'indirizzo email è già stato preso" + +#: taiga/projects/serializers.py:254 +msgid "Invalid role for the project" +msgstr "Ruolo invalido per il progetto" + +#: taiga/projects/serializers.py:352 +msgid "Total milestones must be major or equal to zero" +msgstr "Le tappe totali devono essere maggiori uguali a zero" + +#: taiga/projects/serializers.py:409 +msgid "Default options" +msgstr "Opzioni predefinite" + +#: taiga/projects/serializers.py:410 +msgid "User story's statuses" +msgstr "Stati della storia utente" + +#: taiga/projects/serializers.py:411 +msgid "Points" +msgstr "Punti" + +#: taiga/projects/serializers.py:412 +msgid "Task's statuses" +msgstr "Stati del compito" + +#: taiga/projects/serializers.py:413 +msgid "Issue's statuses" +msgstr "Stati del problema" + +#: taiga/projects/serializers.py:414 +msgid "Issue's types" +msgstr "Tipologie del problema" + +#: taiga/projects/serializers.py:415 +msgid "Priorities" +msgstr "Priorità" + +#: taiga/projects/serializers.py:416 +msgid "Severities" +msgstr "Criticità" + +#: taiga/projects/serializers.py:417 +msgid "Roles" +msgstr "Ruoli" + +#: taiga/projects/services/stats.py:72 +msgid "Future sprint" +msgstr "Sprint futuri" + +#: taiga/projects/services/stats.py:89 +msgid "Project End" +msgstr "Termine di progetto" + +#: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 +msgid "You don't have permissions to set this sprint to this task." +msgstr "Non hai i permessi per aggiungere questo sprint a questo compito" + +#: taiga/projects/tasks/api.py:107 +msgid "You don't have permissions to set this user story to this task." +msgstr "" +"Non hai i permessi per aggiungere questa storia utente a questo compito" + +#: taiga/projects/tasks/api.py:110 +msgid "You don't have permissions to set this status to this task." +msgstr "Non hai i permessi per aggiungere questo stato a questo compito" + +#: taiga/projects/tasks/models.py:56 +msgid "us order" +msgstr "ordine della storia utente" + +#: taiga/projects/tasks/models.py:58 +msgid "taskboard order" +msgstr "ordine del pannello dei compiti" + +#: taiga/projects/tasks/models.py:66 +msgid "is iocaine" +msgstr "è sotto aspirina" + +#: taiga/projects/tasks/validators.py:12 +msgid "There's no task with that id" +msgstr "Non c'è nessun compito con questo ID" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:6 +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:4 +msgid "someone" +msgstr "qualcuno" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:11 +#, python-format +msgid "" +"\n" +"

You have been invited to Taiga!

\n" +"

Hi! %(full_name)s has sent you an invitation to join project " +"%(project)s in Taiga.
Taiga is a Free, open Source Agile Project " +"Management Tool.

\n" +" " +msgstr "" +"\n" +"\n" +"

Sei stato invitato in Taiga!

\n" +"\n" +"

Ciao! %(full_name)s ti ha mandato un invito per partecipare al progetto " +"%(project)s in Taiga.
Taiga è uno strumento agile per la " +"gestione agile e aperta di progetti.

" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 +#, python-format +msgid "" +"\n" +"

And now a few words from the jolly good fellow or sistren
" +"who thought so kindly as to invite you

\n" +"

%(extra)s

\n" +" " +msgstr "" +"\n" +"\n" +"

E adesso qualche parola dal collega
che é stato così gentile " +"da invitarti

\n" +"\n" +"

%(extra)s

" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation to Taiga" +msgstr "Accetta l'invito in Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 +msgid "Accept your invitation" +msgstr "Accetta il tuo invito" + +#: taiga/projects/templates/emails/membership_invitation-body-html.jinja:25 +msgid "The Taiga Team" +msgstr "Il Team di Taiga" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:6 +#, python-format +msgid "" +"\n" +"You, or someone you know, has invited you to Taiga\n" +"\n" +"Hi! %(full_name)s has sent you an invitation to join a project called " +"%(project)s which is being managed on Taiga, a Free, open Source Agile " +"Project Management Tool.\n" +msgstr "" +"\n" +"\n" +"Tu, o qualcuno che ti conosce, è stato invitato in Taiga\n" +"\n" +"\n" +"\n" +"Ciao! %(full_name)sti ha mandato un invito per partecipare al progetto " +"%(project)s gestito con Taiga, uno strumento agile per la gestione agile e " +"aperta di progetti.\n" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 +#, python-format +msgid "" +"\n" +"And now a few words from the jolly good fellow or sistren who thought so " +"kindly as to invite you:\n" +"\n" +"%(extra)s\n" +" " +msgstr "" +"\n" +"\n" +"E adesso qualche parola dal collega che é stato così gentile da invitarti\n" +"\n" +"\n" +"\n" +"%(extra)s" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:18 +msgid "Accept your invitation to Taiga following this link:" +msgstr "Accetta l'invito in Taiga seguendo il seguente link:" + +#: taiga/projects/templates/emails/membership_invitation-body-text.jinja:20 +msgid "" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/projects/templates/emails/membership_invitation-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Invitation to join to the project '%(project)s'\n" +msgstr "" +"\n" +"\n" +"[Taiga] invito a partecipare al progetto '%(project)s'\n" + +#: taiga/projects/templates/emails/membership_notification-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

You have been added to a project

\n" +"

Hello %(full_name)s,
you have been added to the project " +"%(project)s

\n" +" Go to " +"project\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Sei stato aggiunto ad un progetto

\n" +"\n" +"

Salve %(full_name)s,
sei stato aggiunto al progetto %(project)s

\n" +"\n" +"Vai al " +"progetto\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"You have been added to a project\n" +"Hello %(full_name)s,you have been added to the project %(project)s\n" +"\n" +"See project at %(url)s\n" +msgstr "" +"\n" +"\n" +"Sei stato aggiunto ad un progetto\n" +"\n" +"Salve %(full_name)s, sei stato aggiunto al progetto %(project)s\n" +"\n" +"\n" +"\n" +"Guarda il progetto su %(url)s\n" + +#: taiga/projects/templates/emails/membership_notification-subject.jinja:1 +#, python-format +msgid "" +"\n" +"[Taiga] Added to the project '%(project)s'\n" +msgstr "" +"\n" +"\n" +"[Taiga] aggiunto al progetto '%(project)s'\n" + +#. Translators: Name of scrum project template. +#: taiga/projects/translations.py:28 +msgid "Scrum" +msgstr "Scrum" + +#. Translators: Description of scrum project template. +#: taiga/projects/translations.py:30 +msgid "" +"The agile product backlog in Scrum is a prioritized features list, " +"containing short descriptions of all functionality desired in the product. " +"When applying Scrum, it's not necessary to start a project with a lengthy, " +"upfront effort to document all requirements. The Scrum product backlog is " +"then allowed to grow and change as more is learned about the product and its " +"customers" +msgstr "" +"Il prodotto agile \"backlog\" su Scrum è un list periodizzata di features " +"che contiene brevi descrizioni di tutte le funzionalità richieste nel " +"prodotto. Quando applichi Scrum non è necessario iniziare un progetto " +"elencando dettagliatamente tutta la documentazione richiesta. Il backlog " +"Scrum, in questo modo, può crescere e cambiare man mano che si apprendono le " +"caratteristiche del prodotto e dei suoi clienti." + +#. Translators: Name of kanban project template. +#: taiga/projects/translations.py:33 +msgid "Kanban" +msgstr "Kanban" + +#. Translators: Description of kanban project template. +#: taiga/projects/translations.py:35 +msgid "" +"Kanban is a method for managing knowledge work with an emphasis on just-in-" +"time delivery while not overloading the team members. In this approach, the " +"process, from definition of a task to its delivery to the customer, is " +"displayed for participants to see and team members pull work from a queue." +msgstr "" +"Kanban è un metodo per gestire il lavoro sulla conoscenza con un enfasi " +"sulle consegne da fare in tempo, mentre consente di non sovraccaricare i " +"membri del team. Con questo approccio il processo, dalla definizione di un " +"compito alla sua consegna ai clienti, viene mostrato ai partecipanti e ai " +"membri del team, in modo che possano organizzare il lavoro." + +#. Translators: User story point value (value = undefined) +#: taiga/projects/translations.py:43 +msgid "?" +msgstr "?" + +#. Translators: User story point value (value = 0) +#: taiga/projects/translations.py:45 +msgid "0" +msgstr "0" + +#. Translators: User story point value (value = 0.5) +#: taiga/projects/translations.py:47 +msgid "1/2" +msgstr "1/2" + +#. Translators: User story point value (value = 1) +#: taiga/projects/translations.py:49 +msgid "1" +msgstr "1" + +#. Translators: User story point value (value = 2) +#: taiga/projects/translations.py:51 +msgid "2" +msgstr "2" + +#. Translators: User story point value (value = 3) +#: taiga/projects/translations.py:53 +msgid "3" +msgstr "3" + +#. Translators: User story point value (value = 5) +#: taiga/projects/translations.py:55 +msgid "5" +msgstr "5" + +#. Translators: User story point value (value = 8) +#: taiga/projects/translations.py:57 +msgid "8" +msgstr "8" + +#. Translators: User story point value (value = 10) +#: taiga/projects/translations.py:59 +msgid "10" +msgstr "10" + +#. Translators: User story point value (value = 13) +#: taiga/projects/translations.py:61 +msgid "13" +msgstr "13" + +#. Translators: User story point value (value = 20) +#: taiga/projects/translations.py:63 +msgid "20" +msgstr "20" + +#. Translators: User story point value (value = 40) +#: taiga/projects/translations.py:65 +msgid "40" +msgstr "40" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:73 taiga/projects/translations.py:96 +#: taiga/projects/translations.py:112 +msgid "New" +msgstr "Nuovo" + +#. Translators: User story status +#: taiga/projects/translations.py:76 +msgid "Ready" +msgstr "Pronto" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:79 taiga/projects/translations.py:98 +#: taiga/projects/translations.py:114 +msgid "In progress" +msgstr "In via di sviluppo" + +#. Translators: User story status +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:82 taiga/projects/translations.py:100 +#: taiga/projects/translations.py:116 +msgid "Ready for test" +msgstr "Pronto per il test" + +#. Translators: User story status +#: taiga/projects/translations.py:85 +msgid "Done" +msgstr "Fatto" + +#. Translators: User story status +#: taiga/projects/translations.py:88 +msgid "Archived" +msgstr "Archiviato" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:102 taiga/projects/translations.py:118 +msgid "Closed" +msgstr "Concluso" + +#. Translators: Task status +#. Translators: Issue status +#: taiga/projects/translations.py:104 taiga/projects/translations.py:120 +msgid "Needs Info" +msgstr "Necessita di informazioni" + +#. Translators: Issue status +#: taiga/projects/translations.py:122 +msgid "Postponed" +msgstr "Postposto " + +#. Translators: Issue status +#: taiga/projects/translations.py:124 +msgid "Rejected" +msgstr "Rifiutato" + +#. Translators: Issue type +#: taiga/projects/translations.py:132 +msgid "Bug" +msgstr "Bug" + +#. Translators: Issue type +#: taiga/projects/translations.py:134 +msgid "Question" +msgstr "Domanda" + +#. Translators: Issue type +#: taiga/projects/translations.py:136 +msgid "Enhancement" +msgstr "Miglioramento" + +#. Translators: Issue priority +#: taiga/projects/translations.py:144 +msgid "Low" +msgstr "Basso" + +#. Translators: Issue priority +#. Translators: Issue severity +#: taiga/projects/translations.py:146 taiga/projects/translations.py:159 +msgid "Normal" +msgstr "Normale" + +#. Translators: Issue priority +#: taiga/projects/translations.py:148 +msgid "High" +msgstr "Alto" + +#. Translators: Issue severity +#: taiga/projects/translations.py:155 +msgid "Wishlist" +msgstr "Lista dei desideri" + +#. Translators: Issue severity +#: taiga/projects/translations.py:157 +msgid "Minor" +msgstr "Minore" + +#. Translators: Issue severity +#: taiga/projects/translations.py:161 +msgid "Important" +msgstr "Importante" + +#. Translators: Issue severity +#: taiga/projects/translations.py:163 +msgid "Critical" +msgstr "Critico" + +#. Translators: User role +#: taiga/projects/translations.py:170 +msgid "UX" +msgstr "UX" + +#. Translators: User role +#: taiga/projects/translations.py:172 +msgid "Design" +msgstr "Design" + +#. Translators: User role +#: taiga/projects/translations.py:174 +msgid "Front" +msgstr "Front" + +#. Translators: User role +#: taiga/projects/translations.py:176 +msgid "Back" +msgstr "Back" + +#. Translators: User role +#: taiga/projects/translations.py:178 +msgid "Product Owner" +msgstr "Product Owner" + +#. Translators: User role +#: taiga/projects/translations.py:180 +msgid "Stakeholder" +msgstr "Stakeholder" + +#: taiga/projects/userstories/api.py:156 +msgid "You don't have permissions to set this sprint to this user story." +msgstr "" +"Non hai i permessi per aggiungere questo sprint a questa storia utente." + +#: taiga/projects/userstories/api.py:160 +msgid "You don't have permissions to set this status to this user story." +msgstr "Non hai i permessi per aggiungere questo stato a questa storia utente." + +#: taiga/projects/userstories/api.py:254 +#, python-brace-format +msgid "" +"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " +"{subject}\")" +msgstr "" +"Stiamo generando la storia utente [US #{ref} - {subject}](:us:{ref} \"US " +"#{ref} - {subject}\")" + +#: taiga/projects/userstories/models.py:37 +msgid "role" +msgstr "ruolo" + +#: taiga/projects/userstories/models.py:75 +msgid "backlog order" +msgstr "ordine del backlog" + +#: taiga/projects/userstories/models.py:77 +#: taiga/projects/userstories/models.py:79 +msgid "sprint order" +msgstr "ordine dello sprint" + +#: taiga/projects/userstories/models.py:87 +msgid "finish date" +msgstr "data di termine" + +#: taiga/projects/userstories/models.py:95 +msgid "is client requirement" +msgstr "é una richiesta del cliente" + +#: taiga/projects/userstories/models.py:97 +msgid "is team requirement" +msgstr "é una richiesta del team" + +#: taiga/projects/userstories/models.py:102 +msgid "generated from issue" +msgstr "generato da un problema" + +#: taiga/projects/userstories/validators.py:28 +msgid "There's no user story with that id" +msgstr "Non c'è nessuna storia utente con questo ID" + +#: taiga/projects/validators.py:28 +msgid "There's no project with that id" +msgstr "Non c'è nessuno progetto con questo ID" + +#: taiga/projects/validators.py:37 +msgid "There's no user story status with that id" +msgstr "Non c'è nessuno stato della storia utente con questo ID" + +#: taiga/projects/validators.py:46 +msgid "There's no task status with that id" +msgstr "Non c'è nessuno stato del compito con questo ID" + +#: taiga/projects/votes/models.py:28 +msgid "count" +msgstr "conta" + +#: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 +#: taiga/projects/votes/models.py:56 +msgid "Votes" +msgstr "Voti" + +#: taiga/projects/votes/models.py:55 +msgid "Vote" +msgstr "Voto" + +#: taiga/projects/wiki/api.py:66 +msgid "'content' parameter is mandatory" +msgstr "il parametro 'content' è obbligatorio" + +#: taiga/projects/wiki/api.py:69 +msgid "'project_id' parameter is mandatory" +msgstr "Il parametro 'project_id' è obbligatorio" + +#: taiga/projects/wiki/models.py:36 +msgid "last modifier" +msgstr "ultima modificatore" + +#: taiga/projects/wiki/models.py:69 +msgid "href" +msgstr "href" + +#: taiga/timeline/signals.py:67 +msgid "Check the history API for the exact diff" +msgstr "Controlla le API della storie per la differenza esatta" + +#: taiga/users/admin.py:50 +msgid "Personal info" +msgstr "Informazioni personali" + +#: taiga/users/admin.py:52 +msgid "Permissions" +msgstr "Permessi" + +#: taiga/users/admin.py:53 +msgid "Important dates" +msgstr "Date importanti" + +#: taiga/users/api.py:150 taiga/users/api.py:157 +msgid "Invalid username or email" +msgstr "Username o e-mail non validi" + +#: taiga/users/api.py:166 +msgid "Mail sended successful!" +msgstr "Mail inviata con successo!" + +#: taiga/users/api.py:178 taiga/users/api.py:183 +msgid "Token is invalid" +msgstr "Token non valido" + +#: taiga/users/api.py:204 +msgid "Current password parameter needed" +msgstr "E' necessario il parametro della password corrente" + +#: taiga/users/api.py:207 +msgid "New password parameter needed" +msgstr "E' necessario il parametro della nuovo password" + +#: taiga/users/api.py:210 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Lunghezza della password non valida, sono necessari almeno 6 caratteri" + +#: taiga/users/api.py:213 +msgid "Invalid current password" +msgstr "Password corrente non valida" + +#: taiga/users/api.py:229 +msgid "Incomplete arguments" +msgstr "Argomento non valido" + +#: taiga/users/api.py:234 +msgid "Invalid image format" +msgstr "Formato dell'immagine non valido" + +#: taiga/users/api.py:279 +msgid "Duplicated email" +msgstr "E-mail duplicata" + +#: taiga/users/api.py:281 +msgid "Not valid email" +msgstr "E-mail non valida" + +#: taiga/users/api.py:301 taiga/users/api.py:307 +msgid "" +"Invalid, are you sure the token is correct and you didn't use it before?" +msgstr "" +"Non valido, sei sicuro che il token sia corretto e che tu non l'abbia già " +"usato in precedenza?" + +#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +msgid "Invalid, are you sure the token is correct?" +msgstr "Non valido, sei sicuro che il token sia corretto?" + +#: taiga/users/models.py:71 +msgid "superuser status" +msgstr "Stato del super-utente" + +#: taiga/users/models.py:72 +msgid "" +"Designates that this user has all permissions without explicitly assigning " +"them." +msgstr "" +"Definisce che questo utente ha tutti i permessi senza assegnarglieli " +"esplicitamente." + +#: taiga/users/models.py:102 +msgid "username" +msgstr "username" + +#: taiga/users/models.py:103 +msgid "" +"Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" +msgstr "" +"Richiesto. 30 caratteri o meno. Lettere, numeri e caratteri come /./-/_" + +#: taiga/users/models.py:106 +msgid "Enter a valid username." +msgstr "Inserisci un username valido." + +#: taiga/users/models.py:109 +msgid "active" +msgstr "attivo" + +#: taiga/users/models.py:110 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "" +"Definisce se questo utente debba essere trattato come attivo. Deseleziona " +"questo invece di eliminare gli account." + +#: taiga/users/models.py:116 +msgid "biography" +msgstr "biografia" + +#: taiga/users/models.py:119 +msgid "photo" +msgstr "fotografia" + +#: taiga/users/models.py:120 +msgid "date joined" +msgstr "data di inizio partecipazione" + +#: taiga/users/models.py:122 +msgid "default language" +msgstr "lingua predefinita" + +#: taiga/users/models.py:124 +msgid "default theme" +msgstr "tema predefinito" + +#: taiga/users/models.py:126 +msgid "default timezone" +msgstr "timezone predefinita" + +#: taiga/users/models.py:128 +msgid "colorize tags" +msgstr "colora i tag" + +#: taiga/users/models.py:133 +msgid "email token" +msgstr "token e-mail" + +#: taiga/users/models.py:135 +msgid "new email address" +msgstr "nuovo indirizzo e-mail" + +#: taiga/users/models.py:203 +msgid "permissions" +msgstr "permessi" + +#: taiga/users/serializers.py:62 +msgid "invalid" +msgstr "non valido" + +#: taiga/users/serializers.py:73 +msgid "Invalid username. Try with a different one." +msgstr "Nome utente non valido. Provane uno diverso." + +#: taiga/users/services.py:52 taiga/users/services.py:56 +msgid "Username or password does not matches user." +msgstr "Il nome utente o la password non corrispondono all'utente." + +#: taiga/users/templates/emails/change_email-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Change your email

\n" +"

Hello %(full_name)s,
please confirm your email

\n" +" Confirm " +"email\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Cambia la tua email

\n" +"\n" +"

Salve %(full_name)s,
per favore conferma la tua mail

\n" +"\n" +"conferma la " +"mail\n" +"\n" +"

Ignora questo messaggio se non lo hai richiesto

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/users/templates/emails/change_email-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, please confirm your email\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Salve %(full_name)s, per favore conferma la tua mail\n" +"\n" +"\n" +"\n" +"%(url)s\n" +"\n" +"\n" +"\n" +"Ignora questo messaggio se non lo hai richiesto.\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/users/templates/emails/change_email-subject.jinja:1 +msgid "[Taiga] Change email" +msgstr "[Taiga] cambia la mail" + +#: taiga/users/templates/emails/password_recovery-body-html.jinja:4 +#, python-format +msgid "" +"\n" +"

Recover your password

\n" +"

Hello %(full_name)s,
you asked to recover your password

\n" +" Recover your password\n" +"

You can ignore this message if you did not request.

\n" +"

The Taiga Team

\n" +" " +msgstr "" +"\n" +"\n" +"

Recupera la password

\n" +"\n" +"

Salve %(full_name)s,
hai richiesto di poter recuperare la " +"password

\n" +"\n" +"Recupera la password\n" +"\n" +"

Ignora questo messaggio se non lo hai richiesto.

\n" +"\n" +"

Il Team di Taiga

" + +#: taiga/users/templates/emails/password_recovery-body-text.jinja:1 +#, python-format +msgid "" +"\n" +"Hello %(full_name)s, you asked to recover your password\n" +"\n" +"%(url)s\n" +"\n" +"You can ignore this message if you did not request.\n" +"\n" +"---\n" +"The Taiga Team\n" +msgstr "" +"\n" +"\n" +"Salve %(full_name)s, ha richiesto di poter recuperare la tua password \n" +"\n" +"\n" +"\n" +"%(url)s\n" +"\n" +"\n" +"\n" +"Ignora questo messaggio se non lo hai richiesto.\n" +"\n" +"\n" +"\n" +"---\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/users/templates/emails/password_recovery-subject.jinja:1 +msgid "[Taiga] Password recovery" +msgstr "[Taiga] recupero della password" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:6 +msgid "" +"\n" +" \n" +"

Thank you for registering in Taiga

\n" +"

We hope you enjoy it

\n" +"

We built Taiga because we wanted the project management tool " +"that sits open on our computers all day long, to serve as a continued " +"reminder of why we love to collaborate, code and design.

\n" +"

We built it to be beautiful, elegant, simple to use and fun - " +"without forsaking flexibility and power.

\n" +" The taiga Team\n" +" \n" +" " +msgstr "" +"\n" +"\n" +"\n" +"

Grazie per esserti registrato in Taiga

\n" +"\n" +"

Speriamo ti possa divertire

\n" +"\n" +"

Abbiamo progettato Taiga perché volevamo uno strumento di gestione dei " +"progetti che rimanesse aperto sui nostri computer tutto il giorno, per " +"ricordarci perché amiamo collaborare programmare e progettare.

\n" +"\n" +"

Lo abbiamo costruito bello, elegante, semplice e divertente da usare - " +"senza tralasciare flessibilità e potenza.

\n" +"\n" +"Il Team di Taiga\n" +"\n" +"" + +#: taiga/users/templates/emails/registered_user-body-html.jinja:23 +#, python-format +msgid "" +"\n" +" You may remove your account from this service clicking " +"here\n" +" " +msgstr "" +"\n" +"\n" +"Puoi eliminare il tuo account da questo servizio clicca " +"qui" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:1 +msgid "" +"\n" +"Thank you for registering in Taiga\n" +"\n" +"We hope you enjoy it\n" +"\n" +"We built Taiga because we wanted the project management tool that sits open " +"on our computers all day long, to serve as a continued reminder of why we " +"love to collaborate, code and design.\n" +"\n" +"We built it to be beautiful, elegant, simple to use and fun - without " +"forsaking flexibility and power.\n" +"\n" +"--\n" +"The taiga Team\n" +msgstr "" +"\n" +"Grazie per esserti registrato in Taiga\n" +"\n" +"\n" +"\n" +"Speriamo ti possa divertire\n" +"\n" +"\n" +"\n" +"Abbiamo progettato Taiga perché volevamo uno strumento di gestione dei " +"progetti che rimanesse aperto sui nostri computer tutto il giorno, per " +"ricordarci perché amiamo collaborare programmare e progettare.\n" +"\n" +"\n" +"\n" +"Lo abbiamo costruito bello, elegante, semplice e divertente da usare - senza " +"tralasciare flessibilità e potenza.\n" +"\n" +"\n" +"\n" +"--\n" +"\n" +"Il Team di Taiga\n" + +#: taiga/users/templates/emails/registered_user-body-text.jinja:13 +#, python-format +msgid "" +"\n" +"You may remove your account from this service: %(url)s\n" +msgstr "" +"\n" +"\n" +"Puoi eliminare il tuo account da questo servizio: %(url)s\n" + +#: taiga/users/templates/emails/registered_user-subject.jinja:1 +msgid "You've been Taigatized!" +msgstr "Sei stato Taigazzato!" + +#: taiga/users/validators.py:29 +msgid "There's no role with that id" +msgstr "Non c'è nessuno ruolo con questo ID" + +#: taiga/userstorage/api.py:50 +msgid "" +"Duplicate key value violates unique constraint. Key '{}' already exists." +msgstr "" +"Un valore di chiave duplicato viola il vincolo unico. La chiave '{}' esiste " +"già." + +#: taiga/userstorage/models.py:30 +msgid "key" +msgstr "chiave" + +#: taiga/webhooks/models.py:28 taiga/webhooks/models.py:38 +msgid "URL" +msgstr "URL" + +#: taiga/webhooks/models.py:29 +msgid "secret key" +msgstr "chiave segreta" + +#: taiga/webhooks/models.py:39 +msgid "status code" +msgstr "codice di stato" + +#: taiga/webhooks/models.py:40 +msgid "request data" +msgstr "dati della richiesta" + +#: taiga/webhooks/models.py:41 +msgid "request headers" +msgstr "header della richiesta" + +#: taiga/webhooks/models.py:42 +msgid "response data" +msgstr "dati della risposta" + +#: taiga/webhooks/models.py:43 +msgid "response headers" +msgstr "header della risposta" + +#: taiga/webhooks/models.py:44 +msgid "duration" +msgstr "durata" From b5b14c8e46d93e0fe2ec8461a48308bf3503acb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 22 Sep 2015 11:06:06 +0200 Subject: [PATCH 138/190] Fix order in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 442e75c4..bd6f2534 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,10 +19,10 @@ - Add externall apps: now Taiga can integrate with hundreds of applications and service. - Improving searching system, now full text searchs are supported - i18n. + - Add italian (it) translation. - Add polish (pl) translation. - Add portuguese (Brazil) (pt_BR) translation. - Add russian (ru) translation. - - Add italian (it) translation. ### Misc From d5fbe6bafef267b124553086fbb5fc9b13c0ace5 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 23 Sep 2015 09:59:06 +0200 Subject: [PATCH 139/190] Fixing clearing watchers issue --- taiga/projects/notifications/mixins.py | 5 ++-- tests/integration/test_userstories.py | 37 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 27a93d88..03082f3b 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -190,13 +190,14 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): #If that's the case we need to remove it before calling the super method watcher_field = self.fields.pop("watchers", None) self.validate_watchers(attrs, "watchers") - new_watcher_ids = set(attrs.pop("watchers", [])) + new_watcher_ids = attrs.pop("watchers", None) obj = super(WatchedResourceModelSerializer, self).restore_object(attrs, instance) #A partial update can exclude the watchers field or if the new instance can still not be saved - if instance is None or len(new_watcher_ids) == 0: + if instance is None or new_watcher_ids is None: return obj + new_watcher_ids = set(new_watcher_ids) old_watcher_ids = set(obj.get_watchers().values_list("id", flat=True)) adding_watcher_ids = list(new_watcher_ids.difference(old_watcher_ids)) removing_watcher_ids = list(old_watcher_ids.difference(new_watcher_ids)) diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 787656e8..6506f70f 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -477,3 +477,40 @@ def test_update_userstory_respecting_watchers(client): assert response.status_code == 200 assert response.data["subject"] == "Updating test" assert response.data["watchers"] == [watching_user.id] + + +def test_update_userstory_update_watchers(client): + watching_user = f.create_user() + project = f.ProjectFactory.create() + us = f.UserStoryFactory.create(project=project, status__project=project, milestone__project=project) + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) + f.MembershipFactory.create(project=us.project, user=watching_user) + + client.login(user=us.owner) + url = reverse("userstories-detail", kwargs={"pk": us.pk}) + data = {"watchers": [watching_user.id], "version":1} + + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data["watchers"] == [watching_user.id] + watcher_ids = list(us.get_watchers().values_list("id", flat=True)) + assert watcher_ids == [watching_user.id] + + +def test_update_userstory_remove_watchers(client): + watching_user = f.create_user() + project = f.ProjectFactory.create() + us = f.UserStoryFactory.create(project=project, status__project=project, milestone__project=project) + us.add_watcher(watching_user) + f.MembershipFactory.create(project=us.project, user=us.owner, is_owner=True) + f.MembershipFactory.create(project=us.project, user=watching_user) + + client.login(user=us.owner) + url = reverse("userstories-detail", kwargs={"pk": us.pk}) + data = {"watchers": [], "version":1} + + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data["watchers"] == [] + watcher_ids = list(us.get_watchers().values_list("id", flat=True)) + assert watcher_ids == [] From b750236a561ee7eaa600bc4db91f328899d0dadd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 17 Sep 2015 10:33:41 +0200 Subject: [PATCH 140/190] Making total_points and total_sprints nullable values --- .../migrations/0027_auto_20150916_1302.py | 26 +++++++++++++++++ taiga/projects/models.py | 4 +-- taiga/projects/serializers.py | 10 ------- taiga/projects/services/stats.py | 29 ++++++++++++++----- 4 files changed, 50 insertions(+), 19 deletions(-) create mode 100644 taiga/projects/migrations/0027_auto_20150916_1302.py diff --git a/taiga/projects/migrations/0027_auto_20150916_1302.py b/taiga/projects/migrations/0027_auto_20150916_1302.py new file mode 100644 index 00000000..ecdb0f41 --- /dev/null +++ b/taiga/projects/migrations/0027_auto_20150916_1302.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0026_auto_20150911_1237'), + ] + + operations = [ + migrations.AlterField( + model_name='project', + name='total_milestones', + field=models.IntegerField(verbose_name='total of milestones', null=True, blank=True), + preserve_default=True, + ), + migrations.AlterField( + model_name='project', + name='total_story_points', + field=models.FloatField(verbose_name='total story points', null=True, blank=True), + preserve_default=True, + ), + ] diff --git a/taiga/projects/models.py b/taiga/projects/models.py index 482d25da..fe89fac8 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -146,9 +146,9 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): members = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="projects", through="Membership", verbose_name=_("members"), through_fields=("project", "user")) - total_milestones = models.IntegerField(default=0, null=False, blank=False, + total_milestones = models.IntegerField(null=True, blank=True, verbose_name=_("total of milestones")) - total_story_points = models.FloatField(default=0, verbose_name=_("total story points")) + total_story_points = models.FloatField(null=True, blank=True, verbose_name=_("total story points")) is_backlog_activated = models.BooleanField(default=True, null=False, blank=True, verbose_name=_("active backlog panel")) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 2f3b0ab1..9fad7b7b 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -342,16 +342,6 @@ class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, Watched def get_notify_level(self, obj): return getattr(obj, "notify_level", None) - def validate_total_milestones(self, attrs, source): - """ - Check that total_milestones is not null, it's an optional parameter but - not nullable in the model. - """ - value = attrs[source] - if value is None: - raise serializers.ValidationError(_("Total milestones must be major or equal to zero")) - return attrs - class ProjectDetailSerializer(ProjectSerializer): us_statuses = UserStoryStatusSerializer(many=True, required=False) # User Stories diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index b7586cb5..88b2a47c 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -23,6 +23,14 @@ import copy from taiga.projects.history.models import HistoryEntry +def _get_total_story_points(project): + return (project.total_story_points if project.total_story_points is not None else + sum(project.calculated_points["defined"].values())) + +def _get_total_milestones(project): + return (project.total_milestones if project.total_milestones is not None else + project.milestones.count()) + def _get_milestones_stats_for_backlog(project): """ Get collection of stats for each millestone of project. @@ -33,8 +41,12 @@ def _get_milestones_stats_for_backlog(project): current_client_increment = 0 optimal_points_per_sprint = 0 - if project.total_story_points and project.total_milestones: - optimal_points_per_sprint = project.total_story_points / project.total_milestones + + total_story_points = _get_total_story_points(project) + total_milestones = _get_total_milestones(project) + + if total_story_points and total_milestones: + optimal_points_per_sprint = total_story_points / total_milestones future_team_increment = sum(project.future_team_increment.values()) future_client_increment = sum(project.future_client_increment.values()) @@ -50,11 +62,11 @@ def _get_milestones_stats_for_backlog(project): team_increment = 0 client_increment = 0 - for current_milestone in range(0, max(milestones_count, project.total_milestones)): - optimal_points = (project.total_story_points - + for current_milestone in range(0, max(milestones_count, total_milestones)): + optimal_points = (total_story_points - (optimal_points_per_sprint * current_milestone)) - evolution = (project.total_story_points - current_evolution + evolution = (total_story_points - current_evolution if current_evolution is not None else None) if current_milestone < milestones_count: @@ -83,8 +95,8 @@ def _get_milestones_stats_for_backlog(project): } optimal_points -= optimal_points_per_sprint - evolution = (project.total_story_points - current_evolution - if current_evolution is not None and project.total_story_points else None) + evolution = (total_story_points - current_evolution + if current_evolution is not None and total_story_points else None) yield { 'name': _('Project End'), 'optimal': optimal_points, @@ -104,6 +116,7 @@ def _count_status_object(status_obj, counting_storage): counting_storage[status_obj.id]['id'] = status_obj.id counting_storage[status_obj.id]['color'] = status_obj.color + def _count_owned_object(user_obj, counting_storage): if user_obj: if user_obj.id in counting_storage: @@ -126,6 +139,7 @@ def _count_owned_object(user_obj, counting_storage): counting_storage[0]['id'] = 0 counting_storage[0]['color'] = 'black' + def get_stats_for_project_issues(project): project_issues_stats = { 'total_issues': 0, @@ -283,6 +297,7 @@ def _get_closed_tasks_per_member_stats(project): closed_tasks = {p["assigned_to"]: p["count"] for p in closed_tasks} return closed_tasks + def get_member_stats_for_project(project): base_counters = {id: 0 for id in project.members.values_list("id", flat=True)} closed_bugs = base_counters.copy() From 7d9f3914c0f2d95e96b829f8bb01aa7e8c66428b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 23 Sep 2015 11:45:27 +0200 Subject: [PATCH 141/190] Removing thumbnails on attachment deletion --- requirements.txt | 2 +- taiga/projects/attachments/__init__.py | 17 ++++++++++++ taiga/projects/attachments/apps.py | 38 ++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 taiga/projects/attachments/apps.py diff --git a/requirements.txt b/requirements.txt index 946cf442..e116de5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ raven==5.1.1 bleach==1.4 django-ipware==0.1.0 premailer==2.8.1 -django-transactional-cleanup==0.1.14 +django-transactional-cleanup==0.1.15 lxml==3.4.1 git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea pyjwkest==1.0.3 diff --git a/taiga/projects/attachments/__init__.py b/taiga/projects/attachments/__init__.py index e69de29b..fcc59d48 100644 --- a/taiga/projects/attachments/__init__.py +++ b/taiga/projects/attachments/__init__.py @@ -0,0 +1,17 @@ +# 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 . + +default_app_config = "taiga.projects.attachments.apps.AttachmentsAppConfig" diff --git a/taiga/projects/attachments/apps.py b/taiga/projects/attachments/apps.py new file mode 100644 index 00000000..a4497463 --- /dev/null +++ b/taiga/projects/attachments/apps.py @@ -0,0 +1,38 @@ +# 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 easy_thumbnails.files import get_thumbnailer + +from django.apps import AppConfig +from django.apps import apps +from django_transactional_cleanup.signals import cleanup_post_delete + + +def thumbnail_delete(**kwargs): + thumbnailer = get_thumbnailer(kwargs["file"]) + thumbnailer.delete_thumbnails() + + +def connect_attachment_signals(): + cleanup_post_delete.connect(thumbnail_delete) + + +class AttachmentsAppConfig(AppConfig): + name = "taiga.projects.attachments" + verbose_name = "Attachments" + + def ready(self): + connect_attachment_signals() From cde9b77c0d531d7886d7e3c1c8fd9c646b5bc4e1 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 18 Sep 2015 11:45:10 +0200 Subject: [PATCH 142/190] Improving memory usage on project export --- CHANGELOG.md | 3 +- taiga/base/api/settings.py | 2 +- taiga/base/storage.py | 32 ++++++- taiga/base/utils/json.py | 2 + taiga/export_import/api.py | 8 +- .../management/commands/dump_project.py | 8 +- .../management/commands/load_dump.py | 3 +- taiga/export_import/serializers.py | 2 + taiga/export_import/service.py | 87 ++++++++++++++++++- taiga/export_import/tasks.py | 13 +-- tests/integration/test_importer_api.py | 1 - tests/unit/test_export.py | 16 +++- 12 files changed, 151 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6f2534..f625aa5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ - Add endpoints to show the watchers list for issues, tasks and user stories. - Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). - Add externall apps: now Taiga can integrate with hundreds of applications and service. -- Improving searching system, now full text searchs are supported +- Improve searching system, now full text searchs are supported +- Improve export system, now is more efficient and prevents possible crashes with heavy projects. - i18n. - Add italian (it) translation. - Add polish (pl) translation. diff --git a/taiga/base/api/settings.py b/taiga/base/api/settings.py index 69440ec0..34dd9717 100644 --- a/taiga/base/api/settings.py +++ b/taiga/base/api/settings.py @@ -117,7 +117,7 @@ DEFAULTS = { "DATE_INPUT_FORMATS": ( ISO_8601, ), - "DATE_FORMAT": None, + "DATE_FORMAT": ISO_8601, "DATETIME_INPUT_FORMATS": ( ISO_8601, diff --git a/taiga/base/storage.py b/taiga/base/storage.py index acf93306..35e234df 100644 --- a/taiga/base/storage.py +++ b/taiga/base/storage.py @@ -18,7 +18,7 @@ from django.conf import settings from django.core.files import storage import django_sites as sites - +import os class FileSystemStorage(storage.FileSystemStorage): def __init__(self, *args, **kwargs): @@ -30,3 +30,33 @@ class FileSystemStorage(storage.FileSystemStorage): scheme = site.scheme and "{0}:".format(site.scheme) or "" self.base_url = url_tmpl.format(scheme=scheme, domain=site.domain, url=settings.MEDIA_URL) + + def open(self, name, mode='rb'): + """ + Let's create the needed directory structrue before opening the file + """ + + # Create any intermediate directories that do not exist. + # Note that there is a race between os.path.exists and os.makedirs: + # if os.makedirs fails with EEXIST, the directory was created + # concurrently, and we can continue normally. Refs #16082. + directory = os.path.dirname(name) + if not os.path.exists(directory): + try: + if self.directory_permissions_mode is not None: + # os.makedirs applies the global umask, so we reset it, + # for consistency with file_permissions_mode behavior. + old_umask = os.umask(0) + try: + os.makedirs(directory, self.directory_permissions_mode) + finally: + os.umask(old_umask) + else: + os.makedirs(directory) + except OSError as e: + if e.errno != errno.EEXIST: + raise + if not os.path.isdir(directory): + raise IOError("%s exists and is not a directory." % directory) + + return super().open(name, mode=mode) diff --git a/taiga/base/utils/json.py b/taiga/base/utils/json.py index 2f7ac2e5..40132b34 100644 --- a/taiga/base/utils/json.py +++ b/taiga/base/utils/json.py @@ -30,6 +30,8 @@ def loads(data): data = force_text(data) return json.loads(data) +load = json.load + # Some backward compatibility that should # be removed in near future. to_json = dumps diff --git a/taiga/export_import/api.py b/taiga/export_import/api.py index 48e8592c..6fabb96d 100644 --- a/taiga/export_import/api.py +++ b/taiga/export_import/api.py @@ -14,7 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import json import codecs import uuid @@ -26,6 +25,7 @@ from django.conf import settings from django.core.files.storage import default_storage from django.core.files.base import ContentFile +from taiga.base.utils import json from taiga.base.decorators import detail_route, list_route from taiga.base import exceptions as exc from taiga.base import response @@ -67,10 +67,10 @@ class ProjectExporterViewSet(mixins.ImportThrottlingPolicyMixin, GenericViewSet) return response.Accepted({"export_id": task.id}) path = "exports/{}/{}-{}.json".format(project.pk, project.slug, uuid.uuid4().hex) - content = ContentFile(ExportRenderer().render(service.project_to_dict(project), - renderer_context={"indent": 4}).decode('utf-8')) + storage_path = default_storage.path(path) + with default_storage.open(storage_path, mode="w") as outfile: + service.render_project(project, outfile) - default_storage.save(path, content) response_data = { "url": default_storage.url(path) } diff --git a/taiga/export_import/management/commands/dump_project.py b/taiga/export_import/management/commands/dump_project.py index 2ea0d7a3..9728d01c 100644 --- a/taiga/export_import/management/commands/dump_project.py +++ b/taiga/export_import/management/commands/dump_project.py @@ -18,7 +18,10 @@ from django.core.management.base import BaseCommand, CommandError from taiga.projects.models import Project from taiga.export_import.renderers import ExportRenderer -from taiga.export_import.service import project_to_dict +from taiga.export_import.service import render_project + + +import resource class Command(BaseCommand): @@ -34,6 +37,5 @@ class Command(BaseCommand): except Project.DoesNotExist: raise CommandError('Project "%s" does not exist' % project_slug) - data = project_to_dict(project) with open('%s.json'%(project_slug), 'w') as outfile: - self.renderer.render_to_file(data, outfile, renderer_context=self.renderer_context) + render_project(project, outfile) diff --git a/taiga/export_import/management/commands/load_dump.py b/taiga/export_import/management/commands/load_dump.py index 4566cf88..14016c6e 100644 --- a/taiga/export_import/management/commands/load_dump.py +++ b/taiga/export_import/management/commands/load_dump.py @@ -19,8 +19,7 @@ from django.db import transaction from django.db.models import signals from optparse import make_option -import json - +from taiga.base.utils import json from taiga.projects.models import Project from taiga.export_import.renderers import ExportRenderer from taiga.export_import.dump_service import dict_to_project, TaigaImportError diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index 9ea19705..02334510 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -494,6 +494,8 @@ class RolePointsExportSerializer(serializers.ModelSerializer): class MilestoneExportSerializer(WatcheableObjectModelSerializer): owner = UserRelatedField(required=False) modified_date = serializers.DateTimeField(required=False) + estimated_start = serializers.DateField(required=False) + estimated_finish = serializers.DateField(required=False) def __init__(self, *args, **kwargs): project = kwargs.pop('project', None) diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index 25b9be90..ef0b8a22 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -14,20 +14,28 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import uuid +import base64 +import gc +import resource +import os import os.path as path +import uuid + from unidecode import unidecode from django.template.defaultfilters import slugify from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ObjectDoesNotExist +from django.core.files.storage import default_storage +from taiga.base.utils import json from taiga.projects.history.services import make_key_from_model_object, take_snapshot from taiga.timeline.service import build_project_namespace from taiga.projects.references import sequences as seq from taiga.projects.references import models as refs from taiga.projects.userstories.models import RolePoints from taiga.projects.services import find_invited_user +from taiga.base.api.fields import get_component from . import serializers @@ -48,8 +56,81 @@ def add_errors(section, errors): _errors_log[section] = [errors] -def project_to_dict(project): - return serializers.ProjectExportSerializer(project).data +def render_project(project, outfile, chunk_size = 8192): + serializer = serializers.ProjectExportSerializer(project) + outfile.write('{\n') + + first_field = True + for field_name in serializer.fields.keys(): + # Avoid writing "," in the last element + if not first_field: + outfile.write(",\n") + else: + first_field = False + + field = serializer.fields.get(field_name) + field.initialize(parent=serializer, field_name=field_name) + + # These four "special" fields hava attachments so we use them in a special way + if field_name in ["wiki_pages", "user_stories", "tasks", "issues"]: + value = get_component(project, field_name) + outfile.write('"{}": [\n'.format(field_name)) + + attachments_field = field.fields.pop("attachments", None) + if attachments_field: + attachments_field.initialize(parent=field, field_name="attachments") + + first_item = True + for item in value.iterator(): + # Avoid writing "," in the last element + if not first_item: + outfile.write(",\n") + else: + first_item = False + + + dumped_value = json.dumps(field.to_native(item)) + writing_value = dumped_value[:-1]+ ',\n "attachments": [\n' + outfile.write(writing_value) + + first_attachment = True + for attachment in item.attachments.iterator(): + # Avoid writing "," in the last element + if not first_attachment: + outfile.write(",\n") + else: + first_attachment = False + + # Write all the data expect the serialized file + attachment_serializer = serializers.AttachmentExportSerializer(instance=attachment) + attached_file_serializer = attachment_serializer.fields.pop("attached_file") + dumped_value = json.dumps(attachment_serializer.data) + dumped_value = dumped_value[:-1] + ',\n "attached_file":{\n "data":"' + outfile.write(dumped_value) + + # We write the attached_files by chunks so the memory used is not increased + attachment_file = attachment.attached_file + with default_storage.open(attachment_file.name) as f: + while True: + bin_data = f.read(chunk_size) + if not bin_data: + break + + b64_data = base64.b64encode(bin_data).decode('utf-8') + outfile.write(b64_data) + + outfile.write('", \n "name":"{}"}}\n}}'.format(os.path.basename(attachment_file.name))) + + outfile.write(']}') + outfile.flush() + gc.collect() + outfile.write(']') + + else: + value = field.field_to_native(project, field_name) + outfile.write('"{}": {}'.format(field_name, json.dumps(value))) + + outfile.write('}\n') def store_project(data): diff --git a/taiga/export_import/tasks.py b/taiga/export_import/tasks.py index 1698b9c4..f833aef4 100644 --- a/taiga/export_import/tasks.py +++ b/taiga/export_import/tasks.py @@ -29,25 +29,26 @@ from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail from taiga.celery import app -from .service import project_to_dict +from .service import render_project from .dump_service import dict_to_project from .renderers import ExportRenderer logger = logging.getLogger('taiga.export_import') +import resource + @app.task(bind=True) def dump_project(self, user, project): mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) path = "exports/{}/{}-{}.json".format(project.pk, project.slug, self.request.id) + storage_path = default_storage.path(path) try: - content = ExportRenderer().render(project_to_dict(project), renderer_context={"indent": 4}) - content = content.decode('utf-8') - content = ContentFile(content) - - default_storage.save(path, content) url = default_storage.url(path) + with default_storage.open(storage_path, mode="w") as outfile: + render_project(project, outfile) + except Exception: ctx = { "user": user, diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index a0f2219b..d8fe6f50 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -910,7 +910,6 @@ def test_valid_milestone_import(client): assert response.data["watchers"] == [user_watching.email] - def test_milestone_import_duplicated_milestone(client): user = f.UserFactory.create() project = f.ProjectFactory.create(owner=user) diff --git a/tests/unit/test_export.py b/tests/unit/test_export.py index 67de6eac..0a6ba9d6 100644 --- a/tests/unit/test_export.py +++ b/tests/unit/test_export.py @@ -15,21 +15,29 @@ # along with this program. If not, see . import pytest - +import io from .. import factories as f -from taiga.export_import.service import project_to_dict +from taiga.base.utils import json +from taiga.export_import.service import render_project pytestmark = pytest.mark.django_db def test_export_issue_finish_date(client): issue = f.IssueFactory.create(finished_date="2014-10-22") - finish_date = project_to_dict(issue.project)["issues"][0]["finished_date"] + output = io.StringIO() + render_project(issue.project, output) + print(output.getvalue()) + project_data = json.loads(output.getvalue()) + finish_date = project_data["issues"][0]["finished_date"] assert finish_date == "2014-10-22T00:00:00+0000" def test_export_user_story_finish_date(client): user_story = f.UserStoryFactory.create(finish_date="2014-10-22") - finish_date = project_to_dict(user_story.project)["user_stories"][0]["finish_date"] + output = io.StringIO() + render_project(user_story.project, output) + project_data = json.loads(output.getvalue()) + finish_date = project_data["user_stories"][0]["finish_date"] assert finish_date == "2014-10-22T00:00:00+0000" From 60fdd540ab9347757e09ea928f38cb18a56deb29 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 24 Sep 2015 12:53:08 +0200 Subject: [PATCH 143/190] Add sha1 field --- .../migrations/0005_attachment_sha1.py | 20 ++++++++++++++++ taiga/projects/attachments/models.py | 24 +++++++++++++++++-- taiga/projects/attachments/serializers.py | 6 ++--- 3 files changed, 45 insertions(+), 5 deletions(-) create mode 100644 taiga/projects/attachments/migrations/0005_attachment_sha1.py diff --git a/taiga/projects/attachments/migrations/0005_attachment_sha1.py b/taiga/projects/attachments/migrations/0005_attachment_sha1.py new file mode 100644 index 00000000..c67043e1 --- /dev/null +++ b/taiga/projects/attachments/migrations/0005_attachment_sha1.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('attachments', '0004_auto_20150508_1141'), + ] + + operations = [ + migrations.AddField( + model_name='attachment', + name='sha1', + field=models.CharField(default='', verbose_name='sha1', max_length=40, blank=True), + preserve_default=True, + ), + ] \ No newline at end of file diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index 61c590e4..30fa5c22 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -68,6 +68,7 @@ class Attachment(models.Model): upload_to=get_attachment_file_path, verbose_name=_("attached file")) + sha1 = models.CharField(default="", max_length=40, verbose_name=_("sha1"), blank=True) is_deprecated = models.BooleanField(default=False, verbose_name=_("is deprecated")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) @@ -83,11 +84,30 @@ class Attachment(models.Model): ("view_attachment", "Can view attachment"), ) + def __init__(self, *args, **kwargs): + super(Attachment, self).__init__(*args, **kwargs) + self._orig_attached_file = self.attached_file + + def _generate_sha1(self, blocksize=65536): + hasher = hashlib.sha1() + while True: + buff = self.attached_file.file.read(blocksize) + if not buff: + break + hasher.update(buff) + self.sha1 = hasher.hexdigest() + def save(self, *args, **kwargs): if not self._importing or not self.modified_date: self.modified_date = timezone.now() - - return super().save(*args, **kwargs) + if self.attached_file: + if not self.sha1 or self.attached_file != self._orig_attached_file: + self._generate_sha1() + save = super().save(*args, **kwargs) + self._orig_attached_file = self.attached_file + if self.attached_file: + self.attached_file.file.close() + return save def __str__(self): return "Attachment: {}".format(self.id) diff --git a/taiga/projects/attachments/serializers.py b/taiga/projects/attachments/serializers.py index 878b0e3c..f255fa8e 100644 --- a/taiga/projects/attachments/serializers.py +++ b/taiga/projects/attachments/serializers.py @@ -34,8 +34,8 @@ class AttachmentSerializer(serializers.ModelSerializer): model = models.Attachment fields = ("id", "project", "owner", "name", "attached_file", "size", "url", "description", "is_deprecated", "created_date", "modified_date", - "object_id", "order") - read_only_fields = ("owner", "created_date", "modified_date") + "object_id", "order", "sha1") + read_only_fields = ("owner", "created_date", "modified_date", "sha1") def get_url(self, obj): - return obj.attached_file.url + return obj.attached_file.url \ No newline at end of file From 9f47e5f396188c89088856168bda39454923bd41 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 24 Sep 2015 12:54:18 +0200 Subject: [PATCH 144/190] Add generate_sha1 command --- .../attachments/management/commands/__init__.py | 0 .../attachments/management/commands/generate_sha1.py | 12 ++++++++++++ 2 files changed, 12 insertions(+) create mode 100644 taiga/projects/attachments/management/commands/__init__.py create mode 100644 taiga/projects/attachments/management/commands/generate_sha1.py diff --git a/taiga/projects/attachments/management/commands/__init__.py b/taiga/projects/attachments/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/attachments/management/commands/generate_sha1.py b/taiga/projects/attachments/management/commands/generate_sha1.py new file mode 100644 index 00000000..d982681c --- /dev/null +++ b/taiga/projects/attachments/management/commands/generate_sha1.py @@ -0,0 +1,12 @@ +from django.core.management.base import BaseCommand, CommandError +from django.db import transaction + +from taiga.projects.attachments.models import Attachment + + +class Command(BaseCommand): + + @transaction.atomic + def handle(self, *args, **options): + for attachment in Attachment.objects.all(): + attachment.save() From 5e8c6c5fc1174c93e3603c28d64d18d1edd6d9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 25 Sep 2015 18:52:44 +0200 Subject: [PATCH 145/190] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f625aa5c..60a77eaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Add externall apps: now Taiga can integrate with hundreds of applications and service. - Improve searching system, now full text searchs are supported - Improve export system, now is more efficient and prevents possible crashes with heavy projects. +- Add sha1 hash to attachments to verify the integrity of files (thanks to [@astagi](https://github.com/astagi)). - i18n. - Add italian (it) translation. - Add polish (pl) translation. From 24cea7cc8397d15ff74de418a5e57c8cd6117668 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 26 Sep 2015 19:41:28 +0200 Subject: [PATCH 146/190] [i18n] Update locales --- .../external_apps/migrations/0001_initial.py | 2 +- taiga/external_apps/models.py | 2 +- taiga/locale/ca/LC_MESSAGES/django.po | 56 ++--- taiga/locale/de/LC_MESSAGES/django.po | 58 ++--- taiga/locale/en/LC_MESSAGES/django.po | 52 ++--- taiga/locale/es/LC_MESSAGES/django.po | 68 +++--- taiga/locale/fi/LC_MESSAGES/django.po | 58 ++--- taiga/locale/fr/LC_MESSAGES/django.po | 58 ++--- taiga/locale/it/LC_MESSAGES/django.po | 215 +++++++++--------- taiga/locale/nl/LC_MESSAGES/django.po | 58 ++--- taiga/locale/pl/LC_MESSAGES/django.po | 58 ++--- taiga/locale/pt_BR/LC_MESSAGES/django.po | 69 +++--- taiga/locale/ru/LC_MESSAGES/django.po | 58 ++--- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 58 ++--- 14 files changed, 436 insertions(+), 434 deletions(-) diff --git a/taiga/external_apps/migrations/0001_initial.py b/taiga/external_apps/migrations/0001_initial.py index db76cc21..fbbc348b 100644 --- a/taiga/external_apps/migrations/0001_initial.py +++ b/taiga/external_apps/migrations/0001_initial.py @@ -22,7 +22,7 @@ class Migration(migrations.Migration): ('web', models.CharField(null=True, blank=True, max_length=255, verbose_name='web')), ('description', models.TextField(null=True, blank=True, verbose_name='description')), ('next_url', models.TextField(verbose_name='Next url')), - ('key', models.TextField(verbose_name='secret key for cyphering the application tokens')), + ('key', models.TextField(verbose_name='secret key for ciphering the application tokens')), ], options={ 'verbose_name_plural': 'applications', diff --git a/taiga/external_apps/models.py b/taiga/external_apps/models.py index 0e3fcd8d..29eb981c 100644 --- a/taiga/external_apps/models.py +++ b/taiga/external_apps/models.py @@ -38,7 +38,7 @@ class Application(models.Model): next_url = models.TextField(null=False, blank=False, verbose_name=_("Next url")) - key = models.TextField(null=False, blank=False, verbose_name=_("secret key for cyphering the application tokens")) + key = models.TextField(null=False, blank=False, verbose_name=_("secret key for ciphering the application tokens")) class Meta: verbose_name = "application" diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 4adbae8a..364fd824 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" "MIME-Version: 1.0\n" @@ -536,18 +536,18 @@ msgstr "Contingut invàlid. Deu ser {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "" @@ -722,7 +722,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -736,7 +736,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1151,7 +1151,7 @@ msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" +msgid "Partial updates are not supported" msgstr "" #: taiga/projects/attachments/api.py:62 @@ -1201,11 +1201,15 @@ msgstr "Data de modificació" msgid "attached file" msgstr "Arxiu adjunt" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "està obsolet " -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1337,7 +1341,7 @@ msgstr "Borrat" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Sense assignar" @@ -2228,51 +2232,47 @@ msgstr "Aquest e-mail ja està en ús" msgid "Invalid role for the project" msgstr "Rol invàlid per al projecte" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Opcions per defecte" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Estatus d'històries d'usuari" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Punts" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Estatus de tasques" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Estatus d'incidéncies" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Tipus d'incidéncies" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Prioritats" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Severitats" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Rols" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "" @@ -2908,7 +2908,7 @@ msgstr "invàlid" msgid "Invalid username. Try with a different one." msgstr "Nom d'usuari invàlid" -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 2202f8ed..d64b20e4 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -589,18 +589,18 @@ msgstr "Invalider Inhalt. Er muss wie folgt sein: {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Fehler beim Erzeugen der Projekt Export-Datei " -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Fehler beim Laden von Projekt Export-Datei" @@ -868,7 +868,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -882,7 +882,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1321,8 +1321,8 @@ msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Es werden nur Teilaktualisierungen unterstützt" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1371,11 +1371,15 @@ msgstr "Zeitpunkt der Änderung" msgid "attached file" msgstr "Angehangene Datei" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "wurde verworfen" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1507,7 +1511,7 @@ msgstr "entfernt" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Nicht zugewiesen" @@ -2682,51 +2686,47 @@ msgstr "Die E-Mailadresse ist bereits vergeben" msgid "Invalid role for the project" msgstr "Ungültige Rolle für dieses Projekt" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Die Gesamtzahl der Meilensteine muss größer oder gleich Null sein " - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Voreingestellte Optionen" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Status für User-Stories" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Punkte" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Aufgaben Status" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Ticket Status" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Ticket Arten" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Prioritäten" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Gewichtung" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Rollen" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Zukünftiger Sprint" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Projektende" @@ -3389,7 +3389,7 @@ msgstr "ungültig" msgid "Invalid username. Try with a different one." msgstr "Ungültiger Benutzername. Versuchen Sie es mit einem anderen." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Benutzername oder Passwort stimmen mit keinem Benutzer überein." diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index b6a99bcf..785176f5 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -525,18 +525,18 @@ msgstr "" msgid "It contain invalid custom fields." msgstr "" -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "" @@ -711,7 +711,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -725,7 +725,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1124,7 +1124,7 @@ msgid "You don't have permisions to see that." msgstr "" #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" +msgid "Partial updates are not supported" msgstr "" #: taiga/projects/attachments/api.py:62 @@ -1174,11 +1174,15 @@ msgstr "" msgid "attached file" msgstr "" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1310,7 +1314,7 @@ msgstr "" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "" @@ -2195,51 +2199,47 @@ msgstr "" msgid "Invalid role for the project" msgstr "" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "" @@ -2850,7 +2850,7 @@ msgstr "" msgid "Invalid username. Try with a different one." msgstr "" -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index f48dc2c8..7ed50fe0 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" @@ -574,18 +574,18 @@ msgstr "Contenido inválido. Debe ser {\"clave\": \"valor\",...}" msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Erro generando el volcado de datos del proyecto" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Error cargando el volcado de datos del proyecto" @@ -827,7 +827,7 @@ msgstr "[%(project)s] Tu proyecto ha sido importado" #: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 #: taiga/external_apps/api.py:73 msgid "Authentication required" -msgstr "" +msgstr "Se requiere autenticación" #: taiga/external_apps/models.py:33 #: taiga/projects/custom_attributes/models.py:36 @@ -842,13 +842,13 @@ msgstr "nombre" #: taiga/external_apps/models.py:35 msgid "Icon url" -msgstr "" +msgstr "URL del icono" #: taiga/external_apps/models.py:36 msgid "web" -msgstr "" +msgstr "web" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -859,10 +859,10 @@ msgstr "descripción" #: taiga/external_apps/models.py:39 msgid "Next url" -msgstr "" +msgstr "Siguiente URL" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -872,7 +872,7 @@ msgstr "usuario" #: taiga/external_apps/models.py:59 msgid "application" -msgstr "" +msgstr "aplicación" #: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" @@ -1317,8 +1317,8 @@ msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "La actualización parcial no está soportada." +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1367,11 +1367,15 @@ msgstr "fecha modificada" msgid "attached file" msgstr "archivo adjunto" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "está desactualizado" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1503,7 +1507,7 @@ msgstr "borrado" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "No asignado" @@ -2628,51 +2632,47 @@ msgstr "La dirección de email ya está en uso." msgid "Invalid role for the project" msgstr "Rol inválido para el proyecto" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "El número total de sprints debe ser mayor o igual a cero" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Opciones por defecto" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Estados de historia de usuario" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Puntos" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Estado de tareas" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Estados de peticion" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Tipos de petición" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Gravedades" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Roles" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Sprint futuro" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Final de proyecto" @@ -3344,7 +3344,7 @@ msgstr "no válido" msgid "Invalid username. Try with a different one." msgstr "Nombre de usuario inválido. Prueba con otro." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Nombre de usuario o contraseña inválidos." diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 809f11a1..8c0149b5 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" "MIME-Version: 1.0\n" @@ -561,18 +561,18 @@ msgstr "Virheellinen sisältä, pitää olla muodossa {\"avain\": \"arvo\",...}" msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Virhe tiedoston luonnissa" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Virhe tiedoston latauksessa" @@ -832,7 +832,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -846,7 +846,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1280,8 +1280,8 @@ msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Osittaiset päivitykset eivät ole tuettuna." +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1330,11 +1330,15 @@ msgstr "muokkauspvm" msgid "attached file" msgstr "liite" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "on poistettu" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1466,7 +1470,7 @@ msgstr "poistettu" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Tekijä puuttuu" @@ -2597,51 +2601,47 @@ msgstr "Sähköpostiosoite on jo käytössä" msgid "Invalid role for the project" msgstr "Virheellinen rooli projektille" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Virstapylväitä yhteensä pitää olla vähintään 0." - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Oletusoptiot" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Käyttäjätarinatilat" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Pisteet" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Tehtävien tilat" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Pyyntöjen tilat" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "pyyntötyypit" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Kiireellisyydet" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Vakavuudet" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Roolit" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Tuleva kierros" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Projektin loppu" @@ -3309,7 +3309,7 @@ msgstr "virheellinen" msgid "Invalid username. Try with a different one." msgstr "Tuntematon käyttäjänimi, yritä uudelleen." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Käyttäjätunnus tai salasana eivät ole oikein." diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 83837145..59d43e20 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -17,9 +17,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" "MIME-Version: 1.0\n" @@ -591,18 +591,18 @@ msgstr "Format non valide. Il doit être de la forme {\"cle\": \"valeur\",...}" msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Error dans la génération du dump du projet" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Erreur au chargement du dump du projet" @@ -821,7 +821,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -835,7 +835,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1252,8 +1252,8 @@ msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Les mises à jour non partielles ne sont pas supportées" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1302,11 +1302,15 @@ msgstr "état modifié" msgid "attached file" msgstr "pièces jointes" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "est obsolète" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1438,7 +1442,7 @@ msgstr "supprimé" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Non assigné" @@ -2342,51 +2346,47 @@ msgstr "Adresse email déjà existante" msgid "Invalid role for the project" msgstr "Rôle non valide pour le projet" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Le nombre de jalons doit être supérieur ou égal à zéro" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Options par défaut" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Etats de la User Story" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Points" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Etats des tâches" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Statuts des problèmes" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Types de problèmes" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Priorités" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Sévérités" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Rôles" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Sprint futurs" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Fin du projet" @@ -3043,7 +3043,7 @@ msgstr "invalide" msgid "Invalid username. Try with a different one." msgstr "Nom d'utilisateur invalide. Essayez avec un autre nom." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Aucun utilisateur avec ce nom ou ce mot de passe." diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index ddd9b220..b0eb830b 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-20 19:29+0000\n" -"Last-Translator: Andrea Raimondi \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/it/)\n" "MIME-Version: 1.0\n" @@ -43,15 +43,16 @@ msgstr "Username non valido" msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" -"Sono richiesti 255 caratteri, o meno: lettere, numeri e caratteri /./-/_ " +"Sono richiesti 255 caratteri, o meno, contenenti: lettere, numeri e " +"caratteri /./-/_ " #: taiga/auth/services.py:75 msgid "Username is already in use." -msgstr "Il nome utente scelto è già in uso" +msgstr "Il nome utente appena scelto è già utilizzato." #: taiga/auth/services.py:78 msgid "Email is already in use." -msgstr "L'email inserita è già in uso" +msgstr "L'email inserita è già utilizzata." #: taiga/auth/services.py:94 msgid "Token not matches any valid invitation." @@ -63,7 +64,7 @@ msgstr "L'Utente è già registrato." #: taiga/auth/services.py:146 msgid "Membership with user is already exists." -msgstr "L'associazione con l'utente è già avvenuta." +msgstr "L'utente risulta già associato." #: taiga/auth/services.py:172 msgid "Error on creating new user." @@ -91,8 +92,8 @@ msgstr "il valore di '%s' deve essere o vero o falso." msgid "" "Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens." msgstr "" -"Inserisci uno slug valido composto da lettere, numeri, caratteri di " -"sottolineatura o trattini" +"Uno slug valido è composto da lettere, numeri, caratteri di sottolineatura o " +"trattini" #: taiga/base/api/fields.py:532 #, python-format @@ -163,7 +164,7 @@ msgstr "" #: taiga/base/api/fields.py:954 msgid "No file was submitted." -msgstr "Nessun file è stato caricato." +msgstr "Nessun file caricato." #: taiga/base/api/fields.py:955 msgid "The submitted file is empty." @@ -180,8 +181,7 @@ msgstr "" #: taiga/base/api/fields.py:957 msgid "Please either submit a file or check the clear checkbox, not both." msgstr "" -"Per favore carica il file oppure controlla la casella deselezionata, non " -"entrambi. " +"Carica il file oppure controlla la casella deselezionata. Non entrambi. " #: taiga/base/api/fields.py:997 msgid "" @@ -202,7 +202,7 @@ msgstr "Pagina (%(page_number)s) invalida: %(message)s" #: taiga/base/api/permissions.py:61 msgid "Invalid permission definition." -msgstr "Definizione invalida di permesso. " +msgstr "Definizione di permesso non valida." #: taiga/base/api/relations.py:221 #, python-format @@ -246,7 +246,7 @@ msgstr "Dati non validi" #: taiga/base/api/serializers.py:388 msgid "No input provided" -msgstr "Nessun inserimento fornito" +msgstr "Non è stato fornito nessun input" #: taiga/base/api/serializers.py:548 msgid "Cannot create a new item, only existing items may be updated." @@ -280,7 +280,7 @@ msgstr "Richiesta composta erroneamente." #: taiga/base/exceptions.py:58 msgid "Incorrect authentication credentials." -msgstr "Le credenziali non sono corrette" +msgstr "Le credenziali non sono corrette." #: taiga/base/exceptions.py:63 msgid "Authentication credentials were not provided." @@ -320,7 +320,7 @@ msgstr "Errore inaspettato" #: taiga/base/exceptions.py:125 msgid "Not found." -msgstr "Assente." +msgstr "Non trovato." #: taiga/base/exceptions.py:130 msgid "Method not supported for this endpoint." @@ -353,7 +353,7 @@ msgstr "'Progetto' deve essere un valore intero." #: taiga/base/tags.py:25 msgid "tags" -msgstr "tag" +msgstr "tags" #: taiga/base/templates/emails/base-body-html.jinja:6 msgid "Taiga" @@ -470,7 +470,7 @@ msgstr "" #: taiga/base/templates/emails/updates-body-html.jinja:6 msgid "[Taiga] Updates" -msgstr "[Taiga] aggiornamento" +msgstr "[Taiga] Aggiornamenti" #: taiga/base/templates/emails/updates-body-html.jinja:417 msgid "Updates" @@ -503,7 +503,7 @@ msgstr "" #: taiga/export_import/api.py:103 msgid "We needed at least one role" -msgstr "Abbiamo bisogno di almeno un ruolo" +msgstr "C'è bisogno di almeno un ruolo" #: taiga/export_import/api.py:197 msgid "Needed dump file" @@ -524,7 +524,7 @@ msgstr "Errore nell'importazione della lista degli attributi di progetto" #: taiga/export_import/dump_service.py:114 msgid "error importing default project attributes values" msgstr "" -"Errore nell'importazione dei valori degli attributi del progetto predefinito" +"Errore nell'importazione dei valori predefiniti degli attributi del progetto." #: taiga/export_import/dump_service.py:124 msgid "error importing custom attributes" @@ -584,18 +584,18 @@ msgstr "Contenuto errato. Deve essere {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Contiene campi personalizzati invalidi." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Il nome del progetto è duplicato" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Errore nella creazione del dump di progetto" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Errore nel caricamento del dump di progetto" @@ -695,7 +695,7 @@ msgstr "" "

Il tuo progetto %(project)s non è stato esportato correttamente.

\n" "\n" "

Gli amministratori di sistema di Taiga sono stati informati.
Per " -"favore, fai du nuovo un tentativo o contatta il team di supporto a:\n" +"favore, fai di nuovo un tentativo o contatta il team di supporto a:\n" "\n" "%(support_email)s

\n" @@ -734,7 +734,7 @@ msgstr "" "\n" "\n" "\n" -"Per favore, fai du nuovo un tentativo o contatta il team di supporto a: " +"Per favore, fai di nuovo un tentativo o contatta il team di supporto a: " "%(support_email)s\n" "\n" "\n" @@ -770,7 +770,7 @@ msgstr "" "

Il tuo progetto non è stato importato correttamente

\n" "\n" "

Gli amministratori di sistema di Taiga sono stati informati.
Per " -"favore, prova di nuovo o contatta il tema di supporto a:\n" +"favore, prova di nuovo o contatta il team di supporto a:\n" "\n" "%(support_email)s

\n" @@ -917,7 +917,7 @@ msgstr "Url dell'icona" msgid "web" msgstr "web" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -931,8 +931,8 @@ msgid "Next url" msgstr "Url successivo" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" -msgstr "chiave segreta per cifrare i token dell'applicazione" +msgid "secret key for ciphering the application tokens" +msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -993,7 +993,7 @@ msgstr "" #: taiga/feedback/templates/emails/feedback_notification-body-html.jinja:18 #: taiga/users/admin.py:51 msgid "Extra info" -msgstr "Informazioni extra" +msgstr "Informazioni aggiuntive" #: taiga/feedback/templates/emails/feedback_notification-body-text.jinja:1 #, python-format @@ -1033,7 +1033,7 @@ msgstr "" #: taiga/hooks/api.py:52 msgid "The payload is not a valid json" -msgstr "Il carico non è un son valido" +msgstr "Il carico non è un json valido" #: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 #: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 @@ -1163,7 +1163,7 @@ msgstr "" #: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." -msgstr "Problema creato da GitHub" +msgstr "Problema creato su GitHub." #: taiga/hooks/github/event_hooks.py:200 #, python-brace-format @@ -1197,7 +1197,7 @@ msgstr "" #: taiga/hooks/gitlab/event_hooks.py:86 msgid "Status changed from GitLab commit" -msgstr "Lo stato è stato modificato da un commit su GitLab" +msgstr "Lo stato è stato modificato tramite commit su GitLab" #: taiga/hooks/gitlab/event_hooks.py:128 msgid "Created from GitLab" @@ -1249,7 +1249,7 @@ msgstr "Guarda le milestones" #: taiga/permissions/permissions.py:23 taiga/permissions/permissions.py:33 msgid "View user stories" -msgstr "Guarda le user story" +msgstr "Guarda le storie utente" #: taiga/permissions/permissions.py:24 taiga/permissions/permissions.py:35 #: taiga/permissions/permissions.py:63 @@ -1277,15 +1277,15 @@ msgstr "Richiedi l'iscrizione" #: taiga/permissions/permissions.py:39 msgid "Add user story to project" -msgstr "Aggiungi una user story al progetto" +msgstr "Aggiungi una storia utente al progetto" #: taiga/permissions/permissions.py:40 msgid "Add comments to user stories" -msgstr "Aggiungi dei commenti alle user story" +msgstr "Aggiungi dei commenti alle storia utente" #: taiga/permissions/permissions.py:41 msgid "Add comments to tasks" -msgstr "Aggiungi i commenti ai compiti" +msgstr "Aggiungi dei commenti ai compiti" #: taiga/permissions/permissions.py:42 msgid "Add issues" @@ -1293,7 +1293,7 @@ msgstr "Aggiungi i problemi" #: taiga/permissions/permissions.py:43 msgid "Add comments to issues" -msgstr "Aggiungi commenti ai problemi" +msgstr "Aggiungi dei commenti ai problemi" #: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" @@ -1325,19 +1325,19 @@ msgstr "Elimina la tappa" #: taiga/permissions/permissions.py:58 msgid "View user story" -msgstr "Guarda la user story" +msgstr "Guarda la storia utente" #: taiga/permissions/permissions.py:59 msgid "Add user story" -msgstr "Aggiungi una user story" +msgstr "Aggiungi una storia utente" #: taiga/permissions/permissions.py:60 msgid "Modify user story" -msgstr "Modifica una user story" +msgstr "Modifica una storia utente" #: taiga/permissions/permissions.py:61 msgid "Delete user story" -msgstr "Cancella una user story" +msgstr "Cancella una storia utente" #: taiga/permissions/permissions.py:64 msgid "Add task" @@ -1381,7 +1381,7 @@ msgstr "Aggiungi un membro" #: taiga/permissions/permissions.py:87 msgid "Remove member" -msgstr "Rimuovo il membro" +msgstr "Rimuovi il membro" #: taiga/permissions/permissions.py:88 msgid "Delete project" @@ -1405,15 +1405,15 @@ msgstr "La descrizione del template non è valida" #: taiga/projects/api.py:482 taiga/projects/serializers.py:266 msgid "At least one of the user must be an active admin" -msgstr "Almeno uno degli utenti deve essere un amministratore attivo" +msgstr "Almeno uno degli utenti deve essere attivo come amministratore" #: taiga/projects/api.py:512 msgid "You don't have permisions to see that." msgstr "Non hai il permesso di vedere questo elemento." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Aggiornamento non parziale non supportato" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1462,11 +1462,15 @@ msgstr "data modificata" msgid "attached file" msgstr "file allegato" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "non approvato" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1512,7 +1516,7 @@ msgstr "valori" #: taiga/projects/custom_attributes/models.py:97 #: taiga/projects/tasks/models.py:33 taiga/projects/userstories/models.py:34 msgid "user story" -msgstr "user story" +msgstr "storia utente" #: taiga/projects/custom_attributes/models.py:112 msgid "task" @@ -1524,7 +1528,7 @@ msgstr "problema" #: taiga/projects/custom_attributes/serializers.py:57 msgid "Already exists one with the same name." -msgstr "Ne esiste già uno con lo stesso nome" +msgstr "Ne esiste già un altro con lo stesso nome" #: taiga/projects/history/api.py:70 msgid "Comment already deleted" @@ -1598,7 +1602,7 @@ msgstr "rimosso" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Non assegnato" @@ -1975,7 +1979,7 @@ msgstr "Osservato" #: taiga/projects/notifications/services.py:66 #: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" -msgstr "La notifica esiste per l'utente e il progetto specificato " +msgstr "La notifica esiste per l'utente e il progetto specificati" #: taiga/projects/notifications/services.py:428 msgid "Invalid value for notify level" @@ -2003,7 +2007,7 @@ msgstr "" "

Problema #%(ref)s %(subject)s

\n" "\n" "See issue" +"in Taiga\">Guarda il problema" #: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 #, python-format @@ -2223,7 +2227,7 @@ msgstr "" "\n" "

Sprint %(name)s

\n" "\n" -"See " +"Vai allo " "sprint\n" "\n" "

Il Team di Taiga

" @@ -2358,7 +2362,7 @@ msgstr "" "\n" "Salve %(user)s, %(changer)s ha aggiornato un compito di %(project)s\n" "\n" -"Guarda un compito #%(ref)s %(subject)s a %(url)s\n" +"Guarda il compito #%(ref)s %(subject)s a %(url)s\n" #: taiga/projects/notifications/templates/emails/tasks/task-change-subject.jinja:1 #, python-format @@ -2393,7 +2397,7 @@ msgstr "" "

Compito #%(ref)s %(subject)s

\n" "\n" "See task\n" +"\">Vai al compito\n" "\n" "

Il Team di Taiga

" @@ -2450,7 +2454,7 @@ msgstr "" "

Salve %(user)s,
%(changer)s ha eliminato un compito su %(project)s\n" "\n" -"

Task #%(ref)s %(subject)s

\n" +"

Compito #%(ref)s %(subject)s

\n" "\n" "

Il Team di Taiga

" @@ -2503,11 +2507,11 @@ msgid "" msgstr "" "\n" "

User Story aggiornata

\n" -"

Ciao %(user)s,
%(changer)s ha aggiornato una user story in " +"

Ciao %(user)s,
%(changer)s ha aggiornato una storia utente in " "%(project)s

\n" -"

User Story #%(ref)s %(subject)s

\n" -"Guarda la User Story" +"

Storia utente #%(ref)s %(subject)s

\n" +"Guarda la User Story" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-text.jinja:3 #, python-format @@ -2519,8 +2523,8 @@ msgid "" msgstr "" "\n" "User story aggiornata\n" -"Ciao %(user)s, %(changer)s ha aggiornato una user story in %(project)s\n" -"Guarda user story #%(ref)s %(subject)s in %(url)s\n" +"Ciao %(user)s, %(changer)s ha aggiornato una storia utente in %(project)s\n" +"Guarda storia utente #%(ref)s %(subject)s in %(url)s\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-subject.jinja:1 #, python-format @@ -2552,10 +2556,10 @@ msgstr "" "

Salve %(user)s,
%(changer)s ha creato una nuovo storia utente su " "%(project)s

\n" "\n" -"

User Story #%(ref)s %(subject)s

\n" +"

Storia utente #%(ref)s %(subject)s

\n" "\n" "See user story\n" +"%(subject)s\">Vai alla storia utente\n" "\n" "

Il Team di Taiga

" @@ -2613,7 +2617,7 @@ msgstr "" "

Salve %(user)s,
%(changer)s ha eliminato una storia utente su " "%(project)s

\n" "\n" -"

User Story #%(ref)s %(subject)s

\n" +"

Storia utente #%(ref)s %(subject)s

\n" "\n" "

Il Team di Taiga

" @@ -2793,7 +2797,7 @@ msgstr "" "

Salve %(user)s,
%(changer)s ha eliminato una pagina wiki su " "%(project)s

\n" "\n" -"

Wiki page %(page)s

\n" +"

Pagina wiki %(page)s

\n" "\n" "

Il Team di Taiga

" @@ -2860,76 +2864,72 @@ msgstr "versione" #: taiga/projects/permissions.py:39 msgid "You can't leave the project if there are no more owners" -msgstr "Non puoi abbandonare il progetto se non ci sono più proprietari" +msgstr "Non puoi abbandonare il progetto se non ci sono altri proprietari" #: taiga/projects/serializers.py:242 msgid "Email address is already taken" -msgstr "L'indirizzo email è già stato preso" +msgstr "L'indirizzo email è già usato" #: taiga/projects/serializers.py:254 msgid "Invalid role for the project" -msgstr "Ruolo invalido per il progetto" +msgstr "Ruolo di progetto non valido" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Le tappe totali devono essere maggiori uguali a zero" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Opzioni predefinite" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Stati della storia utente" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Punti" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Stati del compito" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Stati del problema" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Tipologie del problema" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Priorità" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Criticità" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Ruoli" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Sprint futuri" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Termine di progetto" #: taiga/projects/tasks/api.py:104 taiga/projects/tasks/api.py:113 msgid "You don't have permissions to set this sprint to this task." -msgstr "Non hai i permessi per aggiungere questo sprint a questo compito" +msgstr "Non hai i permessi per aggiungere questo sprint a questo compito." #: taiga/projects/tasks/api.py:107 msgid "You don't have permissions to set this user story to this task." msgstr "" -"Non hai i permessi per aggiungere questa storia utente a questo compito" +"Non hai i permessi per aggiungere questa storia utente a questo compito." #: taiga/projects/tasks/api.py:110 msgid "You don't have permissions to set this status to this task." -msgstr "Non hai i permessi per aggiungere questo stato a questo compito" +msgstr "Non hai i permessi per aggiungere questo stato a questo compito." #: taiga/projects/tasks/models.py:56 msgid "us order" @@ -2967,8 +2967,8 @@ msgstr "" "

Sei stato invitato in Taiga!

\n" "\n" "

Ciao! %(full_name)s ti ha mandato un invito per partecipare al progetto " -"%(project)s in Taiga.
Taiga è uno strumento agile per la " -"gestione agile e aperta di progetti.

" +"%(project)s in Taiga.
Taiga è uno strumento per la gestione " +"agile e aperta di progetti.

" #: taiga/projects/templates/emails/membership_invitation-body-html.jinja:17 #, python-format @@ -3015,8 +3015,8 @@ msgstr "" "\n" "\n" "Ciao! %(full_name)sti ha mandato un invito per partecipare al progetto " -"%(project)s gestito con Taiga, uno strumento agile per la gestione agile e " -"aperta di progetti.\n" +"%(project)s gestito con Taiga, uno strumento per la gestione agile e aperta " +"di progetti.\n" #: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 #, python-format @@ -3129,11 +3129,11 @@ msgid "" "then allowed to grow and change as more is learned about the product and its " "customers" msgstr "" -"Il prodotto agile \"backlog\" su Scrum è un list periodizzata di features " -"che contiene brevi descrizioni di tutte le funzionalità richieste nel " -"prodotto. Quando applichi Scrum non è necessario iniziare un progetto " -"elencando dettagliatamente tutta la documentazione richiesta. Il backlog " -"Scrum, in questo modo, può crescere e cambiare man mano che si apprendono le " +"Il prodotto agile \"backlog\" su Scrum è una lista di features che contiene " +"brevi descrizioni di tutte le funzionalità richieste nel prodotto. Quando " +"applichi Scrum non è necessario iniziare un progetto elencando " +"dettagliatamente tutta la documentazione necessaria. Il backlog Scrum, in " +"questo modo, può crescere e cambiare man mano che si apprendono le " "caratteristiche del prodotto e dei suoi clienti." #. Translators: Name of kanban project template. @@ -3394,7 +3394,7 @@ msgstr "data di termine" #: taiga/projects/userstories/models.py:95 msgid "is client requirement" -msgstr "é una richiesta del cliente" +msgstr "é un requisito del cliente " #: taiga/projects/userstories/models.py:97 msgid "is team requirement" @@ -3435,11 +3435,11 @@ msgstr "Voto" #: taiga/projects/wiki/api.py:66 msgid "'content' parameter is mandatory" -msgstr "il parametro 'content' è obbligatorio" +msgstr "il parametro 'contenuto' è obbligatorio" #: taiga/projects/wiki/api.py:69 msgid "'project_id' parameter is mandatory" -msgstr "Il parametro 'project_id' è obbligatorio" +msgstr "Il parametro 'ID progetto' è obbligatorio" #: taiga/projects/wiki/models.py:36 msgid "last modifier" @@ -3513,12 +3513,12 @@ msgstr "E-mail non valida" msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -"Non valido, sei sicuro che il token sia corretto e che tu non l'abbia già " +"Non valido. Sei sicuro che il token sia corretto e che tu non l'abbia già " "usato in precedenza?" #: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" -msgstr "Non valido, sei sicuro che il token sia corretto?" +msgstr "Non valido. Sicuro che il token sia corretto?" #: taiga/users/models.py:71 msgid "superuser status" @@ -3534,17 +3534,18 @@ msgstr "" #: taiga/users/models.py:102 msgid "username" -msgstr "username" +msgstr "nome utente" #: taiga/users/models.py:103 msgid "" "Required. 30 characters or fewer. Letters, numbers and /./-/_ characters" msgstr "" -"Richiesto. 30 caratteri o meno. Lettere, numeri e caratteri come /./-/_" +"Richiede 30 caratteri o meno. Deve comprendere: lettere, numeri e caratteri " +"come /./-/_" #: taiga/users/models.py:106 msgid "Enter a valid username." -msgstr "Inserisci un username valido." +msgstr "Inserisci un nome utente valido." #: taiga/users/models.py:109 msgid "active" @@ -3606,7 +3607,7 @@ msgstr "non valido" msgid "Invalid username. Try with a different one." msgstr "Nome utente non valido. Provane uno diverso." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Il nome utente o la password non corrispondono all'utente." @@ -3801,7 +3802,7 @@ msgstr "" "\n" "\n" "\n" -"Speriamo ti possa divertire\n" +"Speriamo ti piaccia!\n" "\n" "\n" "\n" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 8ca843d5..1f1b87f4 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" "MIME-Version: 1.0\n" @@ -574,18 +574,18 @@ msgstr "Ongeldige inhoud. Volgend formaat geldt {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Fout bij genereren project dump" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Fout bij laden project dump" @@ -782,7 +782,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -796,7 +796,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1214,8 +1214,8 @@ msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Niet-partiële updates worden niet ondersteund." +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1264,11 +1264,15 @@ msgstr "gemodifieerde datum" msgid "attached file" msgstr "bijgevoegd bestand" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "is verouderd" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1400,7 +1404,7 @@ msgstr "verwijderd" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Niet toegewezen" @@ -2317,51 +2321,47 @@ msgstr "E-mail adres is al in gebruik" msgid "Invalid role for the project" msgstr "Ongeldige rol voor project" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Totaal milestones moet groter of gelijk zijn aan 0" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Standaard opties" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Status van User story" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Punten" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Statussen van taken" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Statussen van Issues" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Types van issue" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Prioriteiten" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Ernstniveaus" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Rollen" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Toekomstige sprint" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Project einde" @@ -3002,7 +3002,7 @@ msgstr "ongeldig" msgid "Invalid username. Try with a different one." msgstr "Ongeldige gebruikersnaam. Probeer met een andere." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Gebruikersnaam of wachtwoord stemt niet overeen met gebruiker." diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 4dfde0cd..ff7d8e5d 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -574,18 +574,18 @@ msgstr "Niewłaściwa zawartość. Musi to być {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Zawiera niewłaściwe pola niestandardowe." -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Błąd w trakcie generowania zrzutu projektu" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Błąd w trakcie wczytywania zrzutu projektu" @@ -849,7 +849,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -863,7 +863,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1321,8 +1321,8 @@ msgid "You don't have permisions to see that." msgstr "Nie masz uprawnień by to zobaczyć." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Aktualizacje częściowe nie są wspierane" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1371,11 +1371,15 @@ msgstr "data modyfikacji" msgid "attached file" msgstr "załączony plik" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "jest przestarzałe" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1507,7 +1511,7 @@ msgstr "usuniete" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Nieprzypisane" @@ -2653,51 +2657,47 @@ msgstr "Tena adres e-mail jest już w użyciu" msgid "Invalid role for the project" msgstr "Nieprawidłowa rola w projekcie" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Łączna liczba kamieni milowych musi być większa lub równa zero" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Domyślne opcje" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Statusy historyjek użytkownika" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Punkty" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Statusy zadań" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Statusy zgłoszeń" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Typu zgłoszeń" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Priorytety" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Ważność" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Role" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Przyszły sprint" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Zakończenie projektu" @@ -3372,7 +3372,7 @@ msgstr "Niepoprawne" msgid "Invalid username. Try with a different one." msgstr "Niepoprawna nazwa użytkownika. Spróbuj podać inną." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Nazwa użytkownika lub hasło są nieprawidłowe" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 6b2f5701..d6b64317 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -9,15 +9,16 @@ # Hevertton Barbosa , 2015 # Kemel Zaidan , 2015 # Marlon Carvalho , 2015 +# pedromvm , 2015 # Renato Prado , 2015 # Thiago , 2015 msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -579,18 +580,18 @@ msgstr "conteúdo inválido. Deve ser {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "Contém campos personalizados inválidos" -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Nome duplicado para o projeto" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Erro gerando arquivo de restauração do projeto" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Erro carregando arquivo de restauração do projeto" @@ -832,7 +833,7 @@ msgstr "[%(project)s] A restauração do seu projeto foi importada" #: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 #: taiga/external_apps/api.py:73 msgid "Authentication required" -msgstr "" +msgstr "Autenticação necessária" #: taiga/external_apps/models.py:33 #: taiga/projects/custom_attributes/models.py:36 @@ -847,13 +848,13 @@ msgstr "Nome" #: taiga/external_apps/models.py:35 msgid "Icon url" -msgstr "" +msgstr "Ícone da url" #: taiga/external_apps/models.py:36 msgid "web" -msgstr "" +msgstr "web" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -864,10 +865,10 @@ msgstr "descrição" #: taiga/external_apps/models.py:39 msgid "Next url" -msgstr "" +msgstr "Próxima url" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -877,7 +878,7 @@ msgstr "usuário" #: taiga/external_apps/models.py:59 msgid "application" -msgstr "" +msgstr "aplicação" #: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" @@ -1324,8 +1325,8 @@ msgid "You don't have permisions to see that." msgstr "Você não tem permissão para ver isso" #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Updates não parciais não suportados" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1374,11 +1375,15 @@ msgstr "data modificação" msgid "attached file" msgstr "Arquivo anexado" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "está obsoleto" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1510,7 +1515,7 @@ msgstr "removido" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Não-atribuído" @@ -2636,51 +2641,47 @@ msgstr "Endereço de e-mail já utilizado" msgid "Invalid role for the project" msgstr "Função inválida para projeto" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Total de marcos de progresso devem ser maior ou igual a zero" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Opções padrão" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Status de US" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Pontos" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Status de tarefas" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Status de casos" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Tipos de casos" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Severidades" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Funções" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Sprint futuro" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Fim do projeto" @@ -3352,7 +3353,7 @@ msgstr "inválido" msgid "Invalid username. Try with a different one." msgstr "Usuário inválido. Tente com um diferente." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Usuário ou senha não correspondem ao usuário" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 6225928d..01bb217d 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" @@ -581,18 +581,18 @@ msgstr "Неправильные данные. Должны быть в форм msgid "It contain invalid custom fields." msgstr "Содержит неверные специальные поля" -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "Ошибка создания свалочного файла для проекта" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "Ошибка загрузки свалочного файла проекта" @@ -854,7 +854,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -868,7 +868,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1327,8 +1327,8 @@ msgid "You don't have permisions to see that." msgstr "У вас нет разрешения на просмотр." #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "Частичные обновления не поддерживаются" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1377,11 +1377,15 @@ msgstr "изменённая дата" msgid "attached file" msgstr "приложенный файл" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "устаревшее" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1513,7 +1517,7 @@ msgstr "удалено" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "Не назначено" @@ -2649,51 +2653,47 @@ msgstr "Этот почтовый адрес уже используется" msgid "Invalid role for the project" msgstr "Неверная роль для этого проекта" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Количество вех должно быть больше или равно нулю" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "Параметры по умолчанию" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "Статусу пользовательских историй" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "Очки" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "Типы задачи" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "Приоритеты" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "Степени важности" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "Роли" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "Будущий спринт" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "Окончание проекта" @@ -3363,7 +3363,7 @@ msgstr "невалидный" msgid "Invalid username. Try with a different one." msgstr "Неверное имя пользователя. Попробуйте другое." -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "Имя пользователя или пароль не соответствуют пользователю." diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index f7604c7a..089f07cd 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-18 10:52+0200\n" -"PO-Revision-Date: 2015-09-18 08:52+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" @@ -566,18 +566,18 @@ msgstr "無效內容。必須為 {\"key\": \"value\",...}" msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" -#: taiga/export_import/serializers.py:511 +#: taiga/export_import/serializers.py:513 #: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 #: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 #: taiga/projects/serializers.py:172 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" -#: taiga/export_import/tasks.py:54 taiga/export_import/tasks.py:55 +#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 msgid "Error generating project dump" msgstr "產生專案傾倒時出錯" -#: taiga/export_import/tasks.py:88 taiga/export_import/tasks.py:89 +#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 msgid "Error loading project dump" msgstr "載入專案傾倒時出錯" @@ -837,7 +837,7 @@ msgstr "" msgid "web" msgstr "" -#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:73 +#: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 #: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 @@ -851,7 +851,7 @@ msgid "Next url" msgstr "" #: taiga/external_apps/models.py:41 -msgid "secret key for cyphering the application tokens" +msgid "secret key for ciphering the application tokens" msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 @@ -1302,8 +1302,8 @@ msgid "You don't have permisions to see that." msgstr "您無觀看權限" #: taiga/projects/attachments/api.py:47 -msgid "Non partial updates not supported" -msgstr "不支援非部份更新" +msgid "Partial updates are not supported" +msgstr "" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1352,11 +1352,15 @@ msgstr "修改日期" msgid "attached file" msgstr "附加檔案" -#: taiga/projects/attachments/models.py:72 +#: taiga/projects/attachments/models.py:71 +msgid "sha1" +msgstr "" + +#: taiga/projects/attachments/models.py:73 msgid "is deprecated" msgstr "棄用" -#: taiga/projects/attachments/models.py:74 +#: taiga/projects/attachments/models.py:75 #: taiga/projects/custom_attributes/models.py:39 #: taiga/projects/milestones/models.py:54 taiga/projects/models.py:398 #: taiga/projects/models.py:435 taiga/projects/models.py:462 @@ -1488,7 +1492,7 @@ msgstr "移除 " #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:124 taiga/projects/services/stats.py:125 +#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 msgid "Unassigned" msgstr "無指定" @@ -2623,51 +2627,47 @@ msgstr "電子郵件已使用" msgid "Invalid role for the project" msgstr "專案無效的角色" -#: taiga/projects/serializers.py:352 -msgid "Total milestones must be major or equal to zero" -msgstr "Kanban" - -#: taiga/projects/serializers.py:409 +#: taiga/projects/serializers.py:399 msgid "Default options" msgstr "預設選項" -#: taiga/projects/serializers.py:410 +#: taiga/projects/serializers.py:400 msgid "User story's statuses" msgstr "使用者故事狀態" -#: taiga/projects/serializers.py:411 +#: taiga/projects/serializers.py:401 msgid "Points" msgstr "點數" -#: taiga/projects/serializers.py:412 +#: taiga/projects/serializers.py:402 msgid "Task's statuses" msgstr "任務狀態" -#: taiga/projects/serializers.py:413 +#: taiga/projects/serializers.py:403 msgid "Issue's statuses" msgstr "問題狀態" -#: taiga/projects/serializers.py:414 +#: taiga/projects/serializers.py:404 msgid "Issue's types" msgstr "問題類型" -#: taiga/projects/serializers.py:415 +#: taiga/projects/serializers.py:405 msgid "Priorities" msgstr "優先性" -#: taiga/projects/serializers.py:416 +#: taiga/projects/serializers.py:406 msgid "Severities" msgstr "嚴重性" -#: taiga/projects/serializers.py:417 +#: taiga/projects/serializers.py:407 msgid "Roles" msgstr "角色" -#: taiga/projects/services/stats.py:72 +#: taiga/projects/services/stats.py:84 msgid "Future sprint" msgstr "未來之衝刺" -#: taiga/projects/services/stats.py:89 +#: taiga/projects/services/stats.py:101 msgid "Project End" msgstr "專案結束" @@ -3321,7 +3321,7 @@ msgstr "無效" msgid "Invalid username. Try with a different one." msgstr "無效使用者名稱,請重試其它名稱 " -#: taiga/users/services.py:52 taiga/users/services.py:56 +#: taiga/users/services.py:53 taiga/users/services.py:57 msgid "Username or password does not matches user." msgstr "用戶名稱與密碼不符" From f4b11b26fbef0c83124ec3783f8c0c7b11ec0f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 28 Sep 2015 10:59:32 +0200 Subject: [PATCH 147/190] Minor improvements in the command 'generate_sha1' --- .../attachments/management/commands/generate_sha1.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/taiga/projects/attachments/management/commands/generate_sha1.py b/taiga/projects/attachments/management/commands/generate_sha1.py index d982681c..8441d127 100644 --- a/taiga/projects/attachments/management/commands/generate_sha1.py +++ b/taiga/projects/attachments/management/commands/generate_sha1.py @@ -3,10 +3,17 @@ from django.db import transaction from taiga.projects.attachments.models import Attachment +import logging +logger = logging.getLogger(__name__) + class Command(BaseCommand): - @transaction.atomic def handle(self, *args, **options): - for attachment in Attachment.objects.all(): + total = rest = Attachment.objects.all().count() + + for attachment in Attachment.objects.all().order_by("id"): attachment.save() + + rest -= 1 + logger.debug("[{} / {} remaining] - Generate sha1 for attach {}".format(rest, total, attachment.id)) From 1ed9d35d63d73f2cae551891207fa1b76840d6f4 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 29 Sep 2015 12:06:28 +0200 Subject: [PATCH 148/190] Removing watchers change details from issues, tasks and userstories activity --- taiga/projects/history/freeze_impl.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/taiga/projects/history/freeze_impl.py b/taiga/projects/history/freeze_impl.py index d5ecc800..77a81423 100644 --- a/taiga/projects/history/freeze_impl.py +++ b/taiga/projects/history/freeze_impl.py @@ -88,11 +88,6 @@ def _common_users_values(diff): if "owner" in diff: users.update(diff["owner"]) - if "watchers" in diff: - for ids in diff["watchers"]: - if not ids: - continue - users.update(ids) if "assigned_to" in diff: users.update(diff["assigned_to"]) if users: @@ -288,7 +283,6 @@ def userstory_freezer(us) -> dict: "milestone": us.milestone_id, "client_requirement": us.client_requirement, "team_requirement": us.team_requirement, - "watchers": [x.id for x in us.get_watchers()], "attachments": extract_attachments(us), "tags": us.tags, "points": points, @@ -315,7 +309,6 @@ def issue_freezer(issue) -> dict: "description": issue.description, "description_html": mdrender(issue.project, issue.description), "assigned_to": issue.assigned_to_id, - "watchers": [x.pk for x in issue.get_watchers()], "attachments": extract_attachments(issue), "tags": issue.tags, "is_blocked": issue.is_blocked, @@ -337,7 +330,6 @@ def task_freezer(task) -> dict: "description": task.description, "description_html": mdrender(task.project, task.description), "assigned_to": task.assigned_to_id, - "watchers": [x.pk for x in task.get_watchers()], "attachments": extract_attachments(task), "taskboard_order": task.taskboard_order, "us_order": task.us_order, @@ -359,7 +351,6 @@ def wikipage_freezer(wiki) -> dict: "owner": wiki.owner_id, "content": wiki.content, "content_html": mdrender(wiki.project, wiki.content), - "watchers": [x.pk for x in wiki.get_watchers()], "attachments": extract_attachments(wiki), } From 25489ad90112f294dea1ca8f795702f55a40706e Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 29 Sep 2015 12:33:35 +0200 Subject: [PATCH 149/190] Removing watchers change details from issues, tasks and userstories activity, fixing small issue --- taiga/projects/history/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index ae0dd356..09266e6a 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -141,8 +141,8 @@ class HistoryEntry(models.Model): elif key in users_keys: value = [resolve_value("users", x) for x in self.diff[key]] elif key == "watchers": - value = [[resolve_value("users", x) for x in self.diff[key][0]], - [resolve_value("users", x) for x in self.diff[key][1]]] + # Old instances can have watchers + continue elif key == "points": points = {} From 73c148f0fbdbe8d6f07fd278c3155384be3028d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 30 Sep 2015 10:58:28 +0200 Subject: [PATCH 150/190] Improve voters mixins --- taiga/projects/api.py | 5 ++--- taiga/projects/votes/mixins/viewsets.py | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 8d3cfe1f..97e0b0a8 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -44,6 +44,7 @@ from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin from taiga.projects.userstories.models import UserStory, RolePoints from taiga.projects.tasks.models import Task from taiga.projects.issues.models import Issue +from taiga.projects.votes.mixins.viewsets import LikedResourceMixin, FansViewSetMixin from taiga.permissions import service as permissions_service from . import serializers @@ -51,8 +52,6 @@ from . import models from . import permissions from . import services -from .votes.mixins.viewsets import LikedResourceMixin, VotersViewSetMixin - ###################################################### ## Project @@ -292,7 +291,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, ModelCrudViewSet) return response.NoContent() -class ProjectFansViewSet(VotersViewSetMixin, ModelListViewSet): +class ProjectFansViewSet(FansViewSetMixin, ModelListViewSet): permission_classes = (permissions.ProjectFansPermission,) resource_model = models.Project diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py index 3fbb5cdf..3c2bfed5 100644 --- a/taiga/projects/votes/mixins/viewsets.py +++ b/taiga/projects/votes/mixins/viewsets.py @@ -79,7 +79,7 @@ class VotedResourceMixin(BaseVotedResource): return self._remove_vote("downvote", request, pk) -class VotersViewSetMixin: +class BaseVotersViewSetMixin: # Is a ModelListViewSet with two required params: permission_classes and resource_model serializer_class = serializers.VoterSerializer list_serializer_class = serializers.VoterSerializer @@ -112,3 +112,11 @@ class VotersViewSetMixin: def get_queryset(self): resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) return services.get_voters(resource) + + +class VotersViewSetMixin(BaseVotersViewSetMixin): + pass + + +class FansViewSetMixin(BaseVotersViewSetMixin): + pass From 630ea088214f42856e1a1713e5bbef0c89c4f031 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 30 Sep 2015 13:01:43 +0200 Subject: [PATCH 151/190] Small refactor about ignored fields on HistoryEntries --- taiga/projects/history/models.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 09266e6a..4cfcaccc 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -29,6 +29,9 @@ from .choices import HISTORY_TYPE_CHOICES from taiga.base.utils.diff import make_diff as make_diff_from_dicts +# This keys has been removed from freeze_impl so we can have objects where the +# previous diff has value for the attribute and we want to prevent their propagation +IGNORE_DIFF_FIELDS = [ "watchers", "description_diff", "content_diff", "blocked_note_diff"] def _generate_uuid(): return str(uuid.uuid1()) @@ -127,22 +130,12 @@ class HistoryEntry(models.Model): for key in self.diff: value = None - - # Note: Hack to prevent description_diff propagation - # on old HistoryEntry objects. - if key == "description_diff": - continue - elif key == "content_diff": - continue - elif key == "blocked_note_diff": + if key in IGNORE_DIFF_FIELDS: continue elif key in["description", "content", "blocked_note"]: (key, value) = resolve_diff_value(key) elif key in users_keys: value = [resolve_value("users", x) for x in self.diff[key]] - elif key == "watchers": - # Old instances can have watchers - continue elif key == "points": points = {} From ffd33da80d0bb5d5167c823acc31b2dc328434ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 30 Sep 2015 14:04:23 +0200 Subject: [PATCH 152/190] Fix errors in bitbuket hook calls --- taiga/hooks/bitbucket/event_hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 75b2974e..5f58e817 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -38,7 +38,7 @@ class PushEventHook(BaseEventHook): return changes = self.payload.get("push", {}).get('changes', []) - for change in changes: + for change in filter(None, changes): message = change.get("new", {}).get("target", {}).get("message", None) self._process_message(message, None) From 2e5d86228278ef5314f67e62caad59a2f3d9a27d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 1 Oct 2015 13:35:51 +0200 Subject: [PATCH 153/190] Fix errors in bitbuket hook calls (improvements) --- taiga/hooks/bitbucket/event_hooks.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 5f58e817..73008a3e 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -39,7 +39,18 @@ class PushEventHook(BaseEventHook): changes = self.payload.get("push", {}).get('changes', []) for change in filter(None, changes): - message = change.get("new", {}).get("target", {}).get("message", None) + new = change.get("new", None) + if not new: + continue + + target = new.get("target", None) + if not target: + continue + + message = target.get("message", None) + if not message: + continue + self._process_message(message, None) def _process_message(self, message, bitbucket_user): From d0fb4c43e5e3f35a57c155b9921a0e23d22c4b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 2 Oct 2015 13:44:33 +0200 Subject: [PATCH 154/190] Create tags in sample projects --- taiga/projects/management/commands/sample_data.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 6c5a4bcc..5237b478 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -271,7 +271,7 @@ class Command(BaseCommand): project=project)), type=self.sd.db_object_from_queryset(IssueType.objects.filter( project=project)), - tags=self.sd.words(1, 10).split(" ")) + tags=self.sd.words(0, 10).split(" ")) bug.save() @@ -436,7 +436,9 @@ class Command(BaseCommand): anon_permissions=anon_permissions, public_permissions=public_permissions, total_story_points=self.sd.int(600, 3000), - total_milestones=self.sd.int(5,10)) + total_milestones=self.sd.int(5,10), + tags=self.sd.words(1, 10).split(" "))) + project.is_kanban_activated = True project.save() take_snapshot(project, user=project.owner) From 37c9a2fe6e9ffa4b677bcb51755dd83a3078a4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 2 Oct 2015 14:16:19 +0200 Subject: [PATCH 155/190] FIX: Create tags in sample projects --- taiga/projects/management/commands/sample_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 5237b478..26950c60 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -437,7 +437,7 @@ class Command(BaseCommand): public_permissions=public_permissions, total_story_points=self.sd.int(600, 3000), total_milestones=self.sd.int(5,10), - tags=self.sd.words(1, 10).split(" "))) + tags=self.sd.words(1, 10).split(" ")) project.is_kanban_activated = True project.save() From da8024141ba24b91c2d1cf67b8f634d4d55deb0a Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 30 Sep 2015 15:06:15 +0200 Subject: [PATCH 156/190] Refactoring likes, votes, watchers and favourites names --- taiga/projects/api.py | 8 +- taiga/projects/history/services.py | 2 +- taiga/projects/issues/api.py | 2 +- taiga/projects/issues/serializers.py | 6 +- taiga/projects/notifications/mixins.py | 28 ++- taiga/projects/notifications/utils.py | 34 ++- taiga/projects/serializers.py | 8 +- taiga/projects/tasks/serializers.py | 6 +- taiga/projects/userstories/api.py | 2 +- taiga/projects/userstories/serializers.py | 6 +- taiga/projects/votes/mixins/serializers.py | 30 ++- taiga/projects/votes/mixins/viewsets.py | 6 +- taiga/projects/votes/services.py | 1 - taiga/projects/votes/utils.py | 8 +- taiga/users/api.py | 50 +++- taiga/users/permissions.py | 3 +- taiga/users/serializers.py | 33 ++- taiga/users/services.py | 83 +++---- taiga/webhooks/serializers.py | 8 +- .../test_users_resources.py | 16 +- tests/integration/test_star_projects.py | 123 ---------- tests/integration/test_users.py | 228 ++++++++++++++---- tests/integration/test_vote_issues.py | 14 +- tests/integration/test_vote_tasks.py | 14 +- tests/integration/test_vote_userstories.py | 14 +- tests/integration/test_watch_issues.py | 9 +- tests/integration/test_watch_milestones.py | 16 +- tests/integration/test_watch_projects.py | 17 +- tests/integration/test_watch_tasks.py | 9 +- tests/integration/test_watch_userstories.py | 9 +- tests/integration/test_watch_wikipages.py | 16 +- 31 files changed, 459 insertions(+), 350 deletions(-) delete mode 100644 tests/integration/test_star_projects.py diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 97e0b0a8..78993322 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -34,8 +34,8 @@ from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.notifications.mixins import WatchedResourceMixin, WatchersViewSetMixin from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.utils import ( - attach_project_watchers_attrs_to_queryset, - attach_project_is_watched_to_queryset, + attach_project_total_watchers_attrs_to_queryset, + attach_project_is_watcher_to_queryset, attach_notify_level_to_project_queryset) from taiga.projects.mixins.ordering import BulkUpdateOrderMixin @@ -69,9 +69,9 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, ModelCrudViewSet) def get_queryset(self): qs = super().get_queryset() qs = self.attach_votes_attrs_to_queryset(qs) - qs = attach_project_watchers_attrs_to_queryset(qs) + qs = attach_project_total_watchers_attrs_to_queryset(qs) if self.request.user.is_authenticated(): - qs = attach_project_is_watched_to_queryset(qs, self.request.user) + qs = attach_project_is_watcher_to_queryset(qs, self.request.user) qs = attach_notify_level_to_project_queryset(qs, self.request.user) return qs diff --git a/taiga/projects/history/services.py b/taiga/projects/history/services.py index 593eebad..317a9374 100644 --- a/taiga/projects/history/services.py +++ b/taiga/projects/history/services.py @@ -331,7 +331,7 @@ def take_snapshot(obj:object, *, comment:str="", user=None, delete:bool=False): "is_hidden": is_hidden, "is_snapshot": need_real_snapshot, } - + return entry_model.objects.create(**kwargs) diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index e1259e10..e187c3df 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -76,7 +76,7 @@ class IssueViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixin, W "owner", "assigned_to", "subject", - "votes_count") + "total_voters") def get_serializer_class(self, *args, **kwargs): if self.action in ["retrieve", "by_ref"]: diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index 64c83f17..f7978861 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -23,15 +23,15 @@ from taiga.mdrender.service import render as mdrender from taiga.projects.validators import ProjectExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicIssueStatusSerializer -from taiga.projects.notifications.mixins import WatchedResourceModelSerializer -from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin +from taiga.projects.notifications.mixins import EditableWatchedResourceModelSerializer +from taiga.projects.votes.mixins.serializers import VoteResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer from . import models -class IssueSerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): +class IssueSerializer(WatchersValidator, VoteResourceSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(required=False) external_reference = PgArrayField(required=False) is_closed = serializers.Field(source="is_closed") diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index 03082f3b..fe1812f4 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -29,7 +29,10 @@ from taiga.base.api import serializers from taiga.base.api.utils import get_object_or_404 from taiga.base.fields import WatchersField from taiga.projects.notifications import services -from taiga.projects.notifications.utils import attach_watchers_to_queryset, attach_is_watched_to_queryset +from taiga.projects.notifications.utils import (attach_watchers_to_queryset, + attach_is_watcher_to_queryset, + attach_total_watchers_to_queryset) + from taiga.users.models import User from . import models from . serializers import WatcherSerializer @@ -50,8 +53,9 @@ class WatchedResourceMixin: def attach_watchers_attrs_to_queryset(self, queryset): qs = attach_watchers_to_queryset(queryset) + qs = attach_total_watchers_to_queryset(queryset) if self.request.user.is_authenticated(): - qs = attach_is_watched_to_queryset(qs, self.request.user) + qs = attach_is_watcher_to_queryset(qs, self.request.user) return qs @@ -178,12 +182,20 @@ class WatchedModelMixin(object): class WatchedResourceModelSerializer(serializers.ModelSerializer): - is_watched = serializers.SerializerMethodField("get_is_watched") - watchers = WatchersField(required=False) + is_watcher = serializers.SerializerMethodField("get_is_watcher") + total_watchers = serializers.SerializerMethodField("get_total_watchers") - def get_is_watched(self, obj): - # The "is_watched" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "is_watched", False) or False + def get_is_watcher(self, obj): + # The "is_watcher" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "is_watcher", False) or False + + def get_total_watchers(self, obj): + # The "total_watchers" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "total_watchers", 0) or 0 + + +class EditableWatchedResourceModelSerializer(WatchedResourceModelSerializer): + watchers = WatchersField(required=False) def restore_object(self, attrs, instance=None): #watchers is not a field from the model but can be attached in the get_queryset of the viewset. @@ -223,7 +235,7 @@ class WatchedResourceModelSerializer(serializers.ModelSerializer): return super(WatchedResourceModelSerializer, self).to_native(obj) def save(self, **kwargs): - obj = super(WatchedResourceModelSerializer, self).save(**kwargs) + obj = super(EditableWatchedResourceModelSerializer, self).save(**kwargs) self.fields["watchers"] = WatchersField(required=False) obj.watchers = [user.id for user in obj.get_watchers()] return obj diff --git a/taiga/projects/notifications/utils.py b/taiga/projects/notifications/utils.py index 39e6eaff..510ab76a 100644 --- a/taiga/projects/notifications/utils.py +++ b/taiga/projects/notifications/utils.py @@ -40,8 +40,8 @@ def attach_watchers_to_queryset(queryset, as_field="watchers"): return qs -def attach_is_watched_to_queryset(queryset, user, as_field="is_watched"): - """Attach is_watched boolean to each object of the queryset. +def attach_is_watcher_to_queryset(queryset, user, as_field="is_watcher"): + """Attach is_watcher boolean to each object of the queryset. :param user: A users.User object model :param queryset: A Django queryset object. @@ -64,8 +64,28 @@ def attach_is_watched_to_queryset(queryset, user, as_field="is_watched"): return qs -def attach_project_is_watched_to_queryset(queryset, user, as_field="is_watched"): - """Attach is_watched boolean to each object of the projects queryset. +def attach_total_watchers_to_queryset(queryset, as_field="total_watchers"): + """Attach total_watchers boolean to each object of the queryset. + + :param user: A users.User object model + :param queryset: A Django queryset object. + :param as_field: Attach the boolean as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = ("""SELECT count(*) + FROM notifications_watched + WHERE notifications_watched.content_type_id = {type_id} + AND notifications_watched.object_id = {tbl}.id""") + sql = sql.format(type_id=type.id, tbl=model._meta.db_table) + qs = queryset.extra(select={as_field: sql}) + return qs + + +def attach_project_is_watcher_to_queryset(queryset, user, as_field="is_watcher"): + """Attach is_watcher boolean to each object of the projects queryset. :param user: A users.User object model :param queryset: A Django projects queryset object. @@ -89,7 +109,7 @@ def attach_project_is_watched_to_queryset(queryset, user, as_field="is_watched") return qs -def attach_project_watchers_attrs_to_queryset(queryset, as_field="watchers"): +def attach_project_total_watchers_attrs_to_queryset(queryset, as_field="total_watchers"): """Attach watching user ids to each project of the queryset. :param queryset: A Django projects queryset object. @@ -100,10 +120,10 @@ def attach_project_watchers_attrs_to_queryset(queryset, as_field="watchers"): model = queryset.model type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) - sql = ("""SELECT array(SELECT user_id + sql = ("""SELECT count(user_id) FROM notifications_notifypolicy WHERE notifications_notifypolicy.project_id = {tbl}.id - AND notifications_notifypolicy.notify_level != {ignore_notify_level})""") + AND notifications_notifypolicy.notify_level != {ignore_notify_level}""") sql = sql.format(tbl=model._meta.db_table, ignore_notify_level=NotifyLevel.ignore) qs = queryset.extra(select={as_field: sql}) diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 9fad7b7b..5443c0c3 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -25,8 +25,6 @@ from taiga.base.fields import PgArrayField from taiga.base.fields import TagsField from taiga.base.fields import TagsColorsField -from taiga.projects.notifications.validators import WatchersValidator - from taiga.users.services import get_photo_or_gravatar_url from taiga.users.serializers import UserSerializer from taiga.users.serializers import UserBasicInfoSerializer @@ -40,12 +38,12 @@ from taiga.projects.notifications import models as notify_models from . import models from . import services +from .notifications.mixins import WatchedResourceModelSerializer from .validators import ProjectExistsValidator from .custom_attributes.serializers import UserStoryCustomAttributeSerializer from .custom_attributes.serializers import TaskCustomAttributeSerializer from .custom_attributes.serializers import IssueCustomAttributeSerializer -from .notifications.mixins import WatchedResourceModelSerializer -from .votes.mixins.serializers import LikedResourceSerializerMixin +from .votes.mixins.serializers import FanResourceSerializerMixin ###################################################### ## Custom values for selectors @@ -310,7 +308,7 @@ class ProjectMemberSerializer(serializers.ModelSerializer): ## Projects ###################################################### -class ProjectSerializer(WatchersValidator, LikedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): +class ProjectSerializer(FanResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) anon_permissions = PgArrayField(required=False) public_permissions = PgArrayField(required=False) diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 221a188c..81964367 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -27,15 +27,15 @@ from taiga.projects.milestones.validators import SprintExistsValidator from taiga.projects.tasks.validators import TaskExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicTaskStatusSerializerSerializer -from taiga.projects.notifications.mixins import WatchedResourceModelSerializer -from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin +from taiga.projects.notifications.mixins import EditableWatchedResourceModelSerializer +from taiga.projects.votes.mixins.serializers import VoteResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer from . import models -class TaskSerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): +class TaskSerializer(WatchersValidator, VoteResourceSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(required=False, default=[]) external_reference = PgArrayField(required=False) comment = serializers.SerializerMethodField("get_comment") diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 4c4ebf59..9fce8fb0 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -70,7 +70,7 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi order_by_fields = ["backlog_order", "sprint_order", "kanban_order", - "votes_count"] + "total_voters"] # Specific filter used for filtering neighbor user stories _neighbor_tags_filter = filters.TagsFilter('neighbor_tags') diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index a461e57b..35801ba0 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -27,8 +27,8 @@ from taiga.projects.validators import UserStoryStatusExistsValidator from taiga.projects.userstories.validators import UserStoryExistsValidator from taiga.projects.notifications.validators import WatchersValidator from taiga.projects.serializers import BasicUserStoryStatusSerializer -from taiga.projects.notifications.mixins import WatchedResourceModelSerializer -from taiga.projects.votes.mixins.serializers import VotedResourceSerializerMixin +from taiga.projects.notifications.mixins import EditableWatchedResourceModelSerializer +from taiga.projects.votes.mixins.serializers import VoteResourceSerializerMixin from taiga.users.serializers import UserBasicInfoSerializer @@ -45,7 +45,7 @@ class RolePointsField(serializers.WritableField): return json.loads(obj) -class UserStorySerializer(WatchersValidator, VotedResourceSerializerMixin, WatchedResourceModelSerializer, serializers.ModelSerializer): +class UserStorySerializer(WatchersValidator, VoteResourceSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) external_reference = PgArrayField(required=False) points = RolePointsField(source="role_points", required=False) diff --git a/taiga/projects/votes/mixins/serializers.py b/taiga/projects/votes/mixins/serializers.py index a9c95900..c7f2eb5a 100644 --- a/taiga/projects/votes/mixins/serializers.py +++ b/taiga/projects/votes/mixins/serializers.py @@ -17,21 +17,27 @@ from taiga.base.api import serializers -class BaseVotedResourceSerializer(serializers.ModelSerializer): - def get_votes_counter(self, obj): - # The "votes_count" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "votes_count", 0) or 0 +class FanResourceSerializerMixin(serializers.ModelSerializer): + is_fan = serializers.SerializerMethodField("get_is_fan") + total_fans = serializers.SerializerMethodField("get_total_fans") - def get_is_voted(self, obj): + def get_is_fan(self, obj): # The "is_voted" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "is_voted", False) or False + return getattr(obj, "is_voter", False) or False + + def get_total_fans(self, obj): + # The "total_likes" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "total_voters", 0) or 0 -class LikedResourceSerializerMixin(BaseVotedResourceSerializer): - likes = serializers.SerializerMethodField("get_votes_counter") - is_liked = serializers.SerializerMethodField("get_is_voted") +class VoteResourceSerializerMixin(serializers.ModelSerializer): + is_voter = serializers.SerializerMethodField("get_is_voter") + total_voters = serializers.SerializerMethodField("get_total_voters") + def get_is_voter(self, obj): + # The "is_voted" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "is_voter", False) or False -class VotedResourceSerializerMixin(BaseVotedResourceSerializer): - votes = serializers.SerializerMethodField("get_votes_counter") - is_voted = serializers.SerializerMethodField("get_is_voted") + def get_total_voters(self, obj): + # The "total_likes" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "total_voters", 0) or 0 diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py index 3c2bfed5..8567b3d0 100644 --- a/taiga/projects/votes/mixins/viewsets.py +++ b/taiga/projects/votes/mixins/viewsets.py @@ -23,7 +23,7 @@ from taiga.base.decorators import detail_route from taiga.projects.votes import serializers from taiga.projects.votes import services -from taiga.projects.votes.utils import attach_votes_count_to_queryset, attach_is_vote_to_queryset +from taiga.projects.votes.utils import attach_total_voters_to_queryset, attach_is_voter_to_queryset class BaseVotedResource: @@ -33,10 +33,10 @@ class BaseVotedResource: # return self.attach_votes_attrs_to_queryset(qs) def attach_votes_attrs_to_queryset(self, queryset): - qs = attach_votes_count_to_queryset(queryset) + qs = attach_total_voters_to_queryset(queryset) if self.request.user.is_authenticated(): - qs = attach_is_vote_to_queryset(self.request.user, qs) + qs = attach_is_voter_to_queryset(self.request.user, qs) return qs diff --git a/taiga/projects/votes/services.py b/taiga/projects/votes/services.py index ddc1deae..8a6749dc 100644 --- a/taiga/projects/votes/services.py +++ b/taiga/projects/votes/services.py @@ -35,7 +35,6 @@ def add_vote(obj, user): obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) with atomic(): vote, created = Vote.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) - if not created: return diff --git a/taiga/projects/votes/utils.py b/taiga/projects/votes/utils.py index f82b17b0..bc7d9d14 100644 --- a/taiga/projects/votes/utils.py +++ b/taiga/projects/votes/utils.py @@ -18,7 +18,7 @@ from django.apps import apps -def attach_votes_count_to_queryset(queryset, as_field="votes_count"): +def attach_total_voters_to_queryset(queryset, as_field="total_voters"): """Attach votes count to each object of the queryset. Because of laziness of vote objects creation, this makes much simpler and more efficient to @@ -34,8 +34,8 @@ def attach_votes_count_to_queryset(queryset, as_field="votes_count"): """ model = queryset.model type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) - sql = """SELECT coalesce(SUM(votes_count), 0) FROM ( - SELECT coalesce(votes_votes.count, 0) votes_count + sql = """SELECT coalesce(SUM(total_voters), 0) FROM ( + SELECT coalesce(votes_votes.count, 0) total_voters FROM votes_votes WHERE votes_votes.content_type_id = {type_id} AND votes_votes.object_id = {tbl}.id @@ -46,7 +46,7 @@ def attach_votes_count_to_queryset(queryset, as_field="votes_count"): return qs -def attach_is_vote_to_queryset(user, queryset, as_field="is_voted"): +def attach_is_voter_to_queryset(user, queryset, as_field="is_voter"): """Attach is_vote boolean to each object of the queryset. Because of laziness of vote objects creation, this makes much simpler and more efficient to diff --git a/taiga/users/api.py b/taiga/users/api.py index e9696bfd..1d41d5e3 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -113,32 +113,62 @@ class UsersViewSet(ModelCrudViewSet): self.check_permissions(request, "stats", user) return response.Ok(services.get_stats_for_user(user, request.user)) + + def _serialize_liked_content(self, elem, **kwargs): + if elem.get("type") == "project": + serializer = serializers.FanSerializer + else: + serializer = serializers.VotedSerializer + + return serializer(elem, **kwargs) + + @detail_route(methods=["GET"]) - def favourites(self, request, *args, **kwargs): + def watched(self, request, *args, **kwargs): for_user = get_object_or_404(models.User, **kwargs) from_user = request.user - self.check_permissions(request, 'favourites', for_user) + self.check_permissions(request, 'watched', for_user) filters = { "type": request.GET.get("type", None), - "action": request.GET.get("action", None), "q": request.GET.get("q", None), } - self.object_list = services.get_favourites_list(for_user, from_user, **filters) + self.object_list = services.get_watched_list(for_user, from_user, **filters) page = self.paginate_queryset(self.object_list) + elements = page.object_list if page is not None else self.object_list extra_args = { - "many": True, "user_votes": services.get_voted_content_for_user(request.user), "user_watching": services.get_watched_content_for_user(request.user), } - if page is not None: - serializer = serializers.FavouriteSerializer(page.object_list, **extra_args) - else: - serializer = serializers.FavouriteSerializer(self.object_list, **extra_args) + response_data = [self._serialize_liked_content(elem, **extra_args).data for elem in elements] + return response.Ok(response_data) + + + @detail_route(methods=["GET"]) + def liked(self, request, *args, **kwargs): + for_user = get_object_or_404(models.User, **kwargs) + from_user = request.user + self.check_permissions(request, 'liked', for_user) + filters = { + "type": request.GET.get("type", None), + "q": request.GET.get("q", None), + } + + self.object_list = services.get_voted_list(for_user, from_user, **filters) + page = self.paginate_queryset(self.object_list) + elements = page.object_list if page is not None else self.object_list + + extra_args = { + "user_votes": services.get_voted_content_for_user(request.user), + "user_watching": services.get_watched_content_for_user(request.user), + } + + response_data = [self._serialize_liked_content(elem, **extra_args).data for elem in elements] + + return response.Ok(response_data) - return response.Ok(serializer.data) @list_route(methods=["POST"]) def password_recovery(self, request, pk=None): diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index dab7fe0f..d5600d88 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -46,7 +46,8 @@ class UserPermission(TaigaResourcePermission): remove_avatar_perms = IsAuthenticated() change_email_perms = AllowAny() contacts_perms = AllowAny() - favourites_perms = AllowAny() + liked_perms = AllowAny() + watched_perms = AllowAny() class RolesPermission(TaigaResourcePermission): diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 2a9abade..f5e5c6d8 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -155,13 +155,12 @@ class ProjectRoleSerializer(serializers.ModelSerializer): ###################################################### -## Favourite +## Like ###################################################### -class FavouriteSerializer(serializers.Serializer): +class LikeSerializer(serializers.Serializer): type = serializers.CharField() - action = serializers.CharField() id = serializers.IntegerField() ref = serializers.IntegerField() slug = serializers.CharField() @@ -175,11 +174,8 @@ class FavouriteSerializer(serializers.Serializer): created_date = serializers.DateTimeField() is_private = serializers.SerializerMethodField("get_is_private") - is_voted = serializers.SerializerMethodField("get_is_voted") - is_watched = serializers.SerializerMethodField("get_is_watched") - + is_watcher = serializers.SerializerMethodField("get_is_watcher") total_watchers = serializers.IntegerField() - total_votes = serializers.IntegerField() project = serializers.SerializerMethodField("get_project") project_name = serializers.SerializerMethodField("get_project_name") @@ -196,7 +192,7 @@ class FavouriteSerializer(serializers.Serializer): self.user_watching = kwargs.pop("user_watching", {}) # Instantiate the superclass normally - super(FavouriteSerializer, self).__init__(*args, **kwargs) + super(LikeSerializer, self).__init__(*args, **kwargs) def _none_if_project(self, obj, property): type = obj.get("type", "") @@ -230,10 +226,7 @@ class FavouriteSerializer(serializers.Serializer): def get_project_is_private(self, obj): return self._none_if_project(obj, "project_is_private") - def get_is_voted(self, obj): - return obj["id"] in self.user_votes.get(obj["type"], []) - - def get_is_watched(self, obj): + def get_is_watcher(self, obj): return obj["id"] in self.user_watching.get(obj["type"], []) def get_photo(self, obj): @@ -248,3 +241,19 @@ class FavouriteSerializer(serializers.Serializer): def get_tags_color(self, obj): tags = obj.get("tags", []) return [{"name": tc[0], "color": tc[1]} for tc in obj.get("tags_colors", []) if tc[0] in tags] + + + +class FanSerializer(LikeSerializer): + is_fan = serializers.SerializerMethodField("get_is_fan") + total_fans = serializers.IntegerField(source="total_voters") + + def get_is_fan(self, obj): + return obj["id"] in self.user_votes.get(obj["type"], []) + +class VotedSerializer(LikeSerializer): + is_voter = serializers.SerializerMethodField("get_is_voter") + total_voters = serializers.IntegerField() + + def get_is_voter(self, obj): + return obj["id"] in self.user_votes.get(obj["type"], []) diff --git a/taiga/users/services.py b/taiga/users/services.py index e8df5362..d7f498d0 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -188,12 +188,12 @@ def get_watched_content_for_user(user): return user_watches -def _build_favourites_sql_for_projects(for_user): +def _build_watched_sql_for_projects(for_user): sql = """ - SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'watch' AS action, + SELECT projects_project.id AS id, null AS ref, 'project' AS type, tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project, slug AS slug, projects_project.name AS name, null AS subject, - notifications_notifypolicy.created_at as created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to, + notifications_notifypolicy.created_at as created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, null AS assigned_to, null as status, null as status_color FROM notifications_notifypolicy INNER JOIN projects_project @@ -207,11 +207,20 @@ def _build_favourites_sql_for_projects(for_user): LEFT JOIN votes_votes ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) WHERE notifications_notifypolicy.user_id = {for_user_id} - UNION - SELECT projects_project.id AS id, null AS ref, 'project' AS type, 'vote' AS action, + """ + sql = sql.format( + for_user_id=for_user.id, + ignore_notify_level=NotifyLevel.ignore, + project_content_type_id=ContentType.objects.get(app_label="projects", model="project").id) + return sql + + +def _build_voted_sql_for_projects(for_user): + sql = """ + SELECT projects_project.id AS id, null AS ref, 'project' AS type, tags, votes_vote.object_id AS object_id, projects_project.id AS project, slug AS slug, projects_project.name AS name, null AS subject, - votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, null AS assigned_to, + votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, null AS assigned_to, null as status, null as status_color FROM votes_vote INNER JOIN projects_project @@ -233,65 +242,43 @@ def _build_favourites_sql_for_projects(for_user): return sql - -def _build_favourites_sql_for_type(for_user, type, table_name, ref_column="ref", +def _build_sql_for_type(for_user, type, table_name, action_table, ref_column="ref", project_column="project_id", assigned_to_column="assigned_to_id", slug_column="slug", subject_column="subject"): sql = """ - SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'watch' AS action, - tags, notifications_watched.object_id AS object_id, {table_name}.{project_column} AS project, + SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, + tags, {action_table}.object_id AS object_id, {table_name}.{project_column} AS project, {slug_column} AS slug, null AS name, {subject_column} AS subject, - notifications_watched.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_votes, {assigned_to_column} AS assigned_to, + {action_table}.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, {assigned_to_column} AS assigned_to, projects_{type}status.name as status, projects_{type}status.color as status_color - FROM notifications_watched + FROM {action_table} INNER JOIN django_content_type - ON (notifications_watched.content_type_id = django_content_type.id AND django_content_type.model = '{type}') + ON ({action_table}.content_type_id = django_content_type.id AND django_content_type.model = '{type}') INNER JOIN {table_name} - ON ({table_name}.id = notifications_watched.object_id) + ON ({table_name}.id = {action_table}.object_id) INNER JOIN projects_{type}status ON (projects_{type}status.id = {table_name}.status_id) LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id LEFT JOIN votes_votes ON ({table_name}.id = votes_votes.object_id AND django_content_type.id = votes_votes.content_type_id) - WHERE notifications_watched.user_id = {for_user_id} - UNION - SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, 'vote' AS action, - tags, votes_vote.object_id AS object_id, {table_name}.{project_column} AS project, - {slug_column} AS slug, null AS name, {subject_column} AS subject, - votes_vote.created_date, coalesce(watchers, 0) as total_watchers, votes_votes.count total_votes, {assigned_to_column} AS assigned_to, - projects_{type}status.name as status, projects_{type}status.color as status_color - FROM votes_vote - INNER JOIN django_content_type - ON (votes_vote.content_type_id = django_content_type.id AND django_content_type.model = '{type}') - INNER JOIN {table_name} - ON ({table_name}.id = votes_vote.object_id) - INNER JOIN projects_{type}status - ON (projects_{type}status.id = {table_name}.status_id) - LEFT JOIN (SELECT object_id, content_type_id, count(*) watchers FROM notifications_watched GROUP BY object_id, content_type_id) type_watchers - ON {table_name}.id = type_watchers.object_id AND django_content_type.id = type_watchers.content_type_id - LEFT JOIN votes_votes - ON ({table_name}.id = votes_votes.object_id AND django_content_type.id = votes_votes.content_type_id) - WHERE votes_vote.user_id = {for_user_id} + WHERE {action_table}.user_id = {for_user_id} """ sql = sql.format(for_user_id=for_user.id, type=type, table_name=table_name, - ref_column = ref_column, project_column=project_column, - assigned_to_column=assigned_to_column, slug_column=slug_column, - subject_column=subject_column) + action_table=action_table, ref_column = ref_column, + project_column=project_column, assigned_to_column=assigned_to_column, + slug_column=slug_column, subject_column=subject_column) return sql -def get_favourites_list(for_user, from_user, type=None, action=None, q=None): +def _get_favourites_list(for_user, from_user, action_table, project_sql_builder, type=None, q=None): filters_sql = "" and_needed = False if type: filters_sql += " AND type = '{type}' ".format(type=type) - if action: - filters_sql += " AND action = '{action}' ".format(action=action) - if q: filters_sql += """ AND ( to_tsvector('english_nostop', coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('english_nostop', '{q}') @@ -361,10 +348,10 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): for_user_id=for_user.id, from_user_id=from_user_id, filters_sql=filters_sql, - userstories_sql=_build_favourites_sql_for_type(for_user, "userstory", "userstories_userstory", slug_column="null"), - tasks_sql=_build_favourites_sql_for_type(for_user, "task", "tasks_task", slug_column="null"), - issues_sql=_build_favourites_sql_for_type(for_user, "issue", "issues_issue", slug_column="null"), - projects_sql=_build_favourites_sql_for_projects(for_user)) + userstories_sql=_build_sql_for_type(for_user, "userstory", "userstories_userstory", action_table, slug_column="null"), + tasks_sql=_build_sql_for_type(for_user, "task", "tasks_task", action_table, slug_column="null"), + issues_sql=_build_sql_for_type(for_user, "issue", "issues_issue", action_table, slug_column="null"), + projects_sql=project_sql_builder(for_user)) cursor = connection.cursor() cursor.execute(sql) @@ -374,3 +361,11 @@ def get_favourites_list(for_user, from_user, type=None, action=None, q=None): dict(zip([col[0] for col in desc], row)) for row in cursor.fetchall() ] + + +def get_watched_list(for_user, from_user, type=None, q=None): + return _get_favourites_list(for_user, from_user, "notifications_watched", _build_watched_sql_for_projects, type=type, q=q) + + +def get_voted_list(for_user, from_user, type=None, q=None): + return _get_favourites_list(for_user, from_user, "votes_vote", _build_voted_sql_for_projects, type=type, q=q) diff --git a/taiga/webhooks/serializers.py b/taiga/webhooks/serializers.py index 47d0a145..3b0c90c8 100644 --- a/taiga/webhooks/serializers.py +++ b/taiga/webhooks/serializers.py @@ -25,7 +25,7 @@ from taiga.projects.issues import models as issue_models from taiga.projects.milestones import models as milestone_models from taiga.projects.wiki import models as wiki_models from taiga.projects.history import models as history_models -from taiga.projects.notifications.mixins import WatchedResourceModelSerializer +from taiga.projects.notifications.mixins import EditableWatchedResourceModelSerializer from .models import Webhook, WebhookLog @@ -104,7 +104,7 @@ class PointSerializer(serializers.Serializer): return obj.value -class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, +class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) external_reference = PgArrayField(required=False) @@ -121,7 +121,7 @@ class UserStorySerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedR return project.userstorycustomattributes.all() -class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, +class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) owner = UserSerializer() @@ -135,7 +135,7 @@ class TaskSerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResour return project.taskcustomattributes.all() -class IssueSerializer(CustomAttributesValuesWebhookSerializerMixin, WatchedResourceModelSerializer, +class IssueSerializer(CustomAttributesValuesWebhookSerializerMixin, EditableWatchedResourceModelSerializer, serializers.ModelSerializer): tags = TagsField(default=[], required=False) owner = UserSerializer() diff --git a/tests/integration/resources_permissions/test_users_resources.py b/tests/integration/resources_permissions/test_users_resources.py index 761439d1..a78d270d 100644 --- a/tests/integration/resources_permissions/test_users_resources.py +++ b/tests/integration/resources_permissions/test_users_resources.py @@ -289,8 +289,20 @@ def test_user_action_change_email(client, data): assert results == [204, 204, 204] -def test_user_list_votes(client, data): - url = reverse('users-favourites', kwargs={"pk": data.registered_user.pk}) +def test_user_list_watched(client, data): + url = reverse('users-watched', kwargs={"pk": data.registered_user.pk}) + users = [ + None, + data.registered_user, + data.other_user, + data.superuser, + ] + results = helper_test_http_method(client, 'get', url, None, users) + assert results == [200, 200, 200, 200] + + +def test_user_list_liked(client, data): + url = reverse('users-liked', kwargs={"pk": data.registered_user.pk}) users = [ None, data.registered_user, diff --git a/tests/integration/test_star_projects.py b/tests/integration/test_star_projects.py deleted file mode 100644 index 2f2b87aa..00000000 --- a/tests/integration/test_star_projects.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import pytest -from django.core.urlresolvers import reverse - -from .. import factories as f - -pytestmark = pytest.mark.django_db - - -def test_like_project(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-like", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_unlike_project(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-unlike", args=(project.id,)) - - client.login(user) - response = client.post(url) - - assert response.status_code == 200 - - -def test_list_project_fans(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - f.VoteFactory.create(content_object=project, user=user) - url = reverse("project-fans-list", args=(project.id,)) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data[0]['id'] == user.id - - -def test_get_project_fan(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - vote = f.VoteFactory.create(content_object=project, user=user) - url = reverse("project-fans-detail", args=(project.id, vote.user.id)) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data['id'] == vote.user.id - - -def test_get_project_likes(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - url = reverse("projects-detail", args=(project.id,)) - - f.VotesFactory.create(content_object=project, count=5) - - client.login(user) - response = client.get(url) - - assert response.status_code == 200 - assert response.data['likes'] == 5 - - -def test_get_project_is_liked(client): - user = f.UserFactory.create() - project = f.create_project(owner=user) - f.MembershipFactory.create(project=project, user=user, is_owner=True) - f.VotesFactory.create(content_object=project) - url_detail = reverse("projects-detail", args=(project.id,)) - url_like = reverse("projects-like", args=(project.id,)) - url_unlike = reverse("projects-unlike", args=(project.id,)) - - client.login(user) - - response = client.get(url_detail) - assert response.status_code == 200 - assert response.data['likes'] == 0 - assert response.data['is_liked'] == False - - response = client.post(url_like) - assert response.status_code == 200 - - response = client.get(url_detail) - assert response.status_code == 200 - assert response.data['likes'] == 1 - assert response.data['is_liked'] == True - - response = client.post(url_unlike) - assert response.status_code == 200 - - response = client.get(url_detail) - assert response.status_code == 200 - assert response.data['likes'] == 0 - assert response.data['is_liked'] == False diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 4ad11470..302a51a3 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -9,10 +9,10 @@ from .. import factories as f from taiga.base.utils import json from taiga.users import models -from taiga.users.serializers import FavouriteSerializer +from taiga.users.serializers import FanSerializer, VotedSerializer from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS -from taiga.users.services import get_favourites_list +from taiga.users.services import get_watched_list, get_voted_list from easy_thumbnails.files import generate_all_aliases, get_thumbnailer @@ -348,7 +348,7 @@ def test_mail_permissions(client): assert "email" in response.data -def test_get_favourites_list(): +def test_get_watched_list(): fav_user = f.UserFactory() viewer_user = f.UserFactory() @@ -356,58 +356,117 @@ def test_get_favourites_list(): role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) membership = f.MembershipFactory(project=project, role=role, user=fav_user) project.add_watcher(fav_user) + + user_story = f.UserStoryFactory(project=project, subject="Testing user story") + user_story.add_watcher(fav_user) + + task = f.TaskFactory(project=project, subject="Testing task") + task.add_watcher(fav_user) + + issue = f.IssueFactory(project=project, subject="Testing issue") + issue.add_watcher(fav_user) + + assert len(get_watched_list(fav_user, viewer_user)) == 4 + assert len(get_watched_list(fav_user, viewer_user, type="project")) == 1 + assert len(get_watched_list(fav_user, viewer_user, type="userstory")) == 1 + assert len(get_watched_list(fav_user, viewer_user, type="task")) == 1 + assert len(get_watched_list(fav_user, viewer_user, type="issue")) == 1 + assert len(get_watched_list(fav_user, viewer_user, type="unknown")) == 0 + + assert len(get_watched_list(fav_user, viewer_user, q="issue")) == 1 + assert len(get_watched_list(fav_user, viewer_user, q="unexisting text")) == 0 + + +def test_get_voted_list(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) content_type = ContentType.objects.get_for_model(project) f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=project.id, count=1) user_story = f.UserStoryFactory(project=project, subject="Testing user story") - user_story.add_watcher(fav_user) content_type = ContentType.objects.get_for_model(user_story) f.VoteFactory(content_type=content_type, object_id=user_story.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=user_story.id, count=1) task = f.TaskFactory(project=project, subject="Testing task") - task.add_watcher(fav_user) content_type = ContentType.objects.get_for_model(task) f.VoteFactory(content_type=content_type, object_id=task.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=task.id, count=1) issue = f.IssueFactory(project=project, subject="Testing issue") - issue.add_watcher(fav_user) content_type = ContentType.objects.get_for_model(issue) f.VoteFactory(content_type=content_type, object_id=issue.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=issue.id, count=1) - assert len(get_favourites_list(fav_user, viewer_user)) == 8 - assert len(get_favourites_list(fav_user, viewer_user, type="project")) == 2 - assert len(get_favourites_list(fav_user, viewer_user, type="userstory")) == 2 - assert len(get_favourites_list(fav_user, viewer_user, type="task")) == 2 - assert len(get_favourites_list(fav_user, viewer_user, type="issue")) == 2 - assert len(get_favourites_list(fav_user, viewer_user, type="unknown")) == 0 + assert len(get_voted_list(fav_user, viewer_user)) == 4 + assert len(get_voted_list(fav_user, viewer_user, type="project")) == 1 + assert len(get_voted_list(fav_user, viewer_user, type="userstory")) == 1 + assert len(get_voted_list(fav_user, viewer_user, type="task")) == 1 + assert len(get_voted_list(fav_user, viewer_user, type="issue")) == 1 + assert len(get_voted_list(fav_user, viewer_user, type="unknown")) == 0 - assert len(get_favourites_list(fav_user, viewer_user, action="watch")) == 4 - assert len(get_favourites_list(fav_user, viewer_user, action="vote")) == 4 - - assert len(get_favourites_list(fav_user, viewer_user, q="issue")) == 2 - assert len(get_favourites_list(fav_user, viewer_user, q="unexisting text")) == 0 + assert len(get_voted_list(fav_user, viewer_user, q="issue")) == 1 + assert len(get_voted_list(fav_user, viewer_user, q="unexisting text")) == 0 -def test_get_favourites_list_valid_info_for_project(): +def test_get_watched_list_valid_info_for_project(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + project.add_watcher(fav_user) + + raw_project_watch_info = get_watched_list(fav_user, viewer_user)[0] + project_watch_info = FanSerializer(raw_project_watch_info).data + + assert project_watch_info["type"] == "project" + assert project_watch_info["id"] == project.id + assert project_watch_info["ref"] == None + assert project_watch_info["slug"] == project.slug + assert project_watch_info["name"] == project.name + assert project_watch_info["subject"] == None + assert project_watch_info["description"] == project.description + assert project_watch_info["assigned_to"] == None + assert project_watch_info["status"] == None + assert project_watch_info["status_color"] == None + + tags_colors = {tc["name"]:tc["color"] for tc in project_watch_info["tags_colors"]} + assert "test" in tags_colors + assert "tag" in tags_colors + + assert project_watch_info["is_private"] == project.is_private + assert project_watch_info["is_fan"] == False + assert project_watch_info["is_watcher"] == False + assert project_watch_info["total_watchers"] == 1 + assert project_watch_info["total_fans"] == 0 + assert project_watch_info["project"] == None + assert project_watch_info["project_name"] == None + assert project_watch_info["project_slug"] == None + assert project_watch_info["project_is_private"] == None + assert project_watch_info["assigned_to_username"] == None + assert project_watch_info["assigned_to_full_name"] == None + assert project_watch_info["assigned_to_photo"] == None + + +def test_get_voted_list_valid_info_for_project(): fav_user = f.UserFactory() viewer_user = f.UserFactory() - watcher_user = f.UserFactory() project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) - project.add_watcher(watcher_user) content_type = ContentType.objects.get_for_model(project) vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=project.id, count=1) - raw_project_vote_info = get_favourites_list(fav_user, viewer_user)[0] - project_vote_info = FavouriteSerializer(raw_project_vote_info).data + raw_project_vote_info = get_voted_list(fav_user, viewer_user)[0] + project_vote_info = FanSerializer(raw_project_vote_info).data assert project_vote_info["type"] == "project" - assert project_vote_info["action"] == "vote" assert project_vote_info["id"] == project.id assert project_vote_info["ref"] == None assert project_vote_info["slug"] == project.slug @@ -423,10 +482,14 @@ def test_get_favourites_list_valid_info_for_project(): assert "tag" in tags_colors assert project_vote_info["is_private"] == project.is_private - assert project_vote_info["is_voted"] == False - assert project_vote_info["is_watched"] == False - assert project_vote_info["total_watchers"] == 1 - assert project_vote_info["total_votes"] == 1 + + import pprint + pprint.pprint(project_vote_info) + + assert project_vote_info["is_fan"] == False + assert project_vote_info["is_watcher"] == False + assert project_vote_info["total_watchers"] == 0 + assert project_vote_info["total_fans"] == 1 assert project_vote_info["project"] == None assert project_vote_info["project_name"] == None assert project_vote_info["project_slug"] == None @@ -436,10 +499,61 @@ def test_get_favourites_list_valid_info_for_project(): assert project_vote_info["assigned_to_photo"] == None -def test_get_favourites_list_valid_info_for_not_project_types(): +def test_get_watched_list_valid_info_for_not_project_types(): + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + assigned_to_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + + factories = { + "userstory": f.UserStoryFactory, + "task": f.TaskFactory, + "issue": f.IssueFactory + } + + for object_type in factories: + instance = factories[object_type](project=project, + subject="Testing", + tags=["test1", "test2"], + assigned_to=assigned_to_user) + + instance.add_watcher(fav_user) + raw_instance_watch_info = get_watched_list(fav_user, viewer_user, type=object_type)[0] + instance_watch_info = VotedSerializer(raw_instance_watch_info).data + + assert instance_watch_info["type"] == object_type + assert instance_watch_info["id"] == instance.id + assert instance_watch_info["ref"] == instance.ref + assert instance_watch_info["slug"] == None + assert instance_watch_info["name"] == None + assert instance_watch_info["subject"] == instance.subject + assert instance_watch_info["description"] == None + assert instance_watch_info["assigned_to"] == instance.assigned_to.id + assert instance_watch_info["status"] == instance.status.name + assert instance_watch_info["status_color"] == instance.status.color + + tags_colors = {tc["name"]:tc["color"] for tc in instance_watch_info["tags_colors"]} + assert "test1" in tags_colors + assert "test2" in tags_colors + + assert instance_watch_info["is_private"] == None + assert instance_watch_info["is_voter"] == False + assert instance_watch_info["is_watcher"] == False + assert instance_watch_info["total_watchers"] == 1 + assert instance_watch_info["total_voters"] == 0 + assert instance_watch_info["project"] == instance.project.id + assert instance_watch_info["project_name"] == instance.project.name + assert instance_watch_info["project_slug"] == instance.project.slug + assert instance_watch_info["project_is_private"] == instance.project.is_private + assert instance_watch_info["assigned_to_username"] == instance.assigned_to.username + assert instance_watch_info["assigned_to_full_name"] == instance.assigned_to.full_name + assert instance_watch_info["assigned_to_photo"] != "" + + +def test_get_voted_list_valid_info_for_not_project_types(): fav_user = f.UserFactory() viewer_user = f.UserFactory() - watcher_user = f.UserFactory() assigned_to_user = f.UserFactory() project = f.ProjectFactory(is_private=False, name="Testing project") @@ -456,16 +570,14 @@ def test_get_favourites_list_valid_info_for_not_project_types(): tags=["test1", "test2"], assigned_to=assigned_to_user) - instance.add_watcher(watcher_user) content_type = ContentType.objects.get_for_model(instance) vote = f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) - raw_instance_vote_info = get_favourites_list(fav_user, viewer_user, type=object_type)[0] - instance_vote_info = FavouriteSerializer(raw_instance_vote_info).data + raw_instance_vote_info = get_voted_list(fav_user, viewer_user, type=object_type)[0] + instance_vote_info = VotedSerializer(raw_instance_vote_info).data assert instance_vote_info["type"] == object_type - assert instance_vote_info["action"] == "vote" assert instance_vote_info["id"] == instance.id assert instance_vote_info["ref"] == instance.ref assert instance_vote_info["slug"] == None @@ -481,10 +593,10 @@ def test_get_favourites_list_valid_info_for_not_project_types(): assert "test2" in tags_colors assert instance_vote_info["is_private"] == None - assert instance_vote_info["is_voted"] == False - assert instance_vote_info["is_watched"] == False - assert instance_vote_info["total_watchers"] == 1 - assert instance_vote_info["total_votes"] == 3 + assert instance_vote_info["is_voter"] == False + assert instance_vote_info["is_watcher"] == False + assert instance_vote_info["total_watchers"] == 0 + assert instance_vote_info["total_voters"] == 3 assert instance_vote_info["project"] == instance.project.id assert instance_vote_info["project_name"] == instance.project.name assert instance_vote_info["project_slug"] == instance.project.slug @@ -494,7 +606,41 @@ def test_get_favourites_list_valid_info_for_not_project_types(): assert instance_vote_info["assigned_to_photo"] != "" -def test_get_favourites_list_permissions(): +def test_get_watched_list_permissions(): + fav_user = f.UserFactory() + viewer_unpriviliged_user = f.UserFactory() + viewer_priviliged_user = f.UserFactory() + + project = f.ProjectFactory(is_private=True, name="Testing project") + project.add_watcher(fav_user) + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=viewer_priviliged_user) + + user_story = f.UserStoryFactory(project=project, subject="Testing user story") + user_story.add_watcher(fav_user) + + task = f.TaskFactory(project=project, subject="Testing task") + task.add_watcher(fav_user) + + issue = f.IssueFactory(project=project, subject="Testing issue") + issue.add_watcher(fav_user) + + #If the project is private a viewer user without any permission shouldn' see + # any vote + assert len(get_watched_list(fav_user, viewer_unpriviliged_user)) == 0 + + #If the project is private but the viewer user has permissions the votes should + # be accesible + assert len(get_watched_list(fav_user, viewer_priviliged_user)) == 4 + + #If the project is private but has the required anon permissions the votes should + # be accesible by any user too + project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] + project.save() + assert len(get_watched_list(fav_user, viewer_unpriviliged_user)) == 4 + + +def test_get_voted_list_permissions(): fav_user = f.UserFactory() viewer_unpriviliged_user = f.UserFactory() viewer_priviliged_user = f.UserFactory() @@ -523,14 +669,14 @@ def test_get_favourites_list_permissions(): #If the project is private a viewer user without any permission shouldn' see # any vote - assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 0 + assert len(get_voted_list(fav_user, viewer_unpriviliged_user)) == 0 #If the project is private but the viewer user has permissions the votes should # be accesible - assert len(get_favourites_list(fav_user, viewer_priviliged_user)) == 4 + assert len(get_voted_list(fav_user, viewer_priviliged_user)) == 4 #If the project is private but has the required anon permissions the votes should # be accesible by any user too project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] project.save() - assert len(get_favourites_list(fav_user, viewer_unpriviliged_user)) == 4 + assert len(get_voted_list(fav_user, viewer_unpriviliged_user)) == 4 diff --git a/tests/integration/test_vote_issues.py b/tests/integration/test_vote_issues.py index 8faca67b..5076b227 100644 --- a/tests/integration/test_vote_issues.py +++ b/tests/integration/test_vote_issues.py @@ -85,7 +85,7 @@ def test_get_issue_votes(client): response = client.get(url) assert response.status_code == 200 - assert response.data['votes'] == 5 + assert response.data['total_voters'] == 5 def test_get_issue_is_voted(client): @@ -101,21 +101,21 @@ def test_get_issue_is_voted(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False response = client.post(url_upvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 1 - assert response.data['is_voted'] == True + assert response.data['total_voters'] == 1 + assert response.data['is_voter'] == True response = client.post(url_downvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False diff --git a/tests/integration/test_vote_tasks.py b/tests/integration/test_vote_tasks.py index a5cdea56..7d71eff4 100644 --- a/tests/integration/test_vote_tasks.py +++ b/tests/integration/test_vote_tasks.py @@ -87,7 +87,7 @@ def test_get_task_votes(client): response = client.get(url) assert response.status_code == 200 - assert response.data['votes'] == 5 + assert response.data['total_voters'] == 5 def test_get_task_is_voted(client): @@ -103,21 +103,21 @@ def test_get_task_is_voted(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False response = client.post(url_upvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 1 - assert response.data['is_voted'] == True + assert response.data['total_voters'] == 1 + assert response.data['is_voter'] == True response = client.post(url_downvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False diff --git a/tests/integration/test_vote_userstories.py b/tests/integration/test_vote_userstories.py index ab863df3..492f8121 100644 --- a/tests/integration/test_vote_userstories.py +++ b/tests/integration/test_vote_userstories.py @@ -86,7 +86,7 @@ def test_get_user_story_votes(client): response = client.get(url) assert response.status_code == 200 - assert response.data['votes'] == 5 + assert response.data['total_voters'] == 5 def test_get_user_story_is_voted(client): @@ -102,21 +102,21 @@ def test_get_user_story_is_voted(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False response = client.post(url_upvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 1 - assert response.data['is_voted'] == True + assert response.data['total_voters'] == 1 + assert response.data['is_voter'] == True response = client.post(url_downvote) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['votes'] == 0 - assert response.data['is_voted'] == False + assert response.data['total_voters'] == 0 + assert response.data['is_voter'] == False diff --git a/tests/integration/test_watch_issues.py b/tests/integration/test_watch_issues.py index 09ba4f7b..91c64ec8 100644 --- a/tests/integration/test_watch_issues.py +++ b/tests/integration/test_watch_issues.py @@ -89,9 +89,10 @@ def test_get_issue_watchers(client): assert response.status_code == 200 assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_issue_is_watched(client): +def test_get_issue_is_watcher(client): user = f.UserFactory.create() issue = f.IssueFactory(owner=user) f.MembershipFactory.create(project=issue.project, user=user, is_owner=True) @@ -104,7 +105,7 @@ def test_get_issue_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 @@ -112,7 +113,7 @@ def test_get_issue_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 @@ -120,4 +121,4 @@ def test_get_issue_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_milestones.py b/tests/integration/test_watch_milestones.py index 72fea24d..f744086c 100644 --- a/tests/integration/test_watch_milestones.py +++ b/tests/integration/test_watch_milestones.py @@ -88,10 +88,10 @@ def test_get_milestone_watchers(client): response = client.get(url) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_milestone_is_watched(client): +def test_get_milestone_is_watcher(client): user = f.UserFactory.create() milestone = f.MilestoneFactory(owner=user) f.MembershipFactory.create(project=milestone.project, user=user, is_owner=True) @@ -103,21 +103,21 @@ def test_get_milestone_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['total_watchers'] == 1 + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index bbf58a0c..8b1bf4da 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -121,10 +121,10 @@ def test_get_project_watchers(client): response = client.get(url) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_project_is_watched(client): +def test_get_project_is_watcher(client): user = f.UserFactory.create() project = f.ProjectFactory.create(is_private=False, anon_permissions=list(map(lambda x: x[0], ANON_PERMISSIONS)), @@ -139,21 +139,22 @@ def test_get_project_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['total_watchers'] == 1 + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_tasks.py b/tests/integration/test_watch_tasks.py index 7444a948..449cba88 100644 --- a/tests/integration/test_watch_tasks.py +++ b/tests/integration/test_watch_tasks.py @@ -89,9 +89,10 @@ def test_get_task_watchers(client): assert response.status_code == 200 assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_task_is_watched(client): +def test_get_task_is_watcher(client): user = f.UserFactory.create() task = f.TaskFactory(owner=user) f.MembershipFactory.create(project=task.project, user=user, is_owner=True) @@ -104,7 +105,7 @@ def test_get_task_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 @@ -112,7 +113,7 @@ def test_get_task_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 @@ -120,4 +121,4 @@ def test_get_task_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_userstories.py b/tests/integration/test_watch_userstories.py index cad86151..86ae3ef0 100644 --- a/tests/integration/test_watch_userstories.py +++ b/tests/integration/test_watch_userstories.py @@ -89,9 +89,10 @@ def test_get_user_story_watchers(client): assert response.status_code == 200 assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_user_story_is_watched(client): +def test_get_user_story_is_watcher(client): user = f.UserFactory.create() user_story = f.UserStoryFactory(owner=user) f.MembershipFactory.create(project=user_story.project, user=user, is_owner=True) @@ -104,7 +105,7 @@ def test_get_user_story_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 @@ -112,7 +113,7 @@ def test_get_user_story_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 @@ -120,4 +121,4 @@ def test_get_user_story_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_wikipages.py b/tests/integration/test_watch_wikipages.py index c4f96bb6..a26fe2a4 100644 --- a/tests/integration/test_watch_wikipages.py +++ b/tests/integration/test_watch_wikipages.py @@ -88,10 +88,10 @@ def test_get_wikipage_watchers(client): response = client.get(url) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] + assert response.data['total_watchers'] == 1 -def test_get_wikipage_is_watched(client): +def test_get_wikipage_is_watcher(client): user = f.UserFactory.create() wikipage = f.WikiPageFactory(owner=user) f.MembershipFactory.create(project=wikipage.project, user=user, is_owner=True) @@ -103,21 +103,21 @@ def test_get_wikipage_is_watched(client): response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + assert response.data['is_watcher'] == False response = client.post(url_watch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [user.id] - assert response.data['is_watched'] == True + assert response.data['total_watchers'] == 1 + assert response.data['is_watcher'] == True response = client.post(url_unwatch) assert response.status_code == 200 response = client.get(url_detail) assert response.status_code == 200 - assert response.data['watchers'] == [] - assert response.data['is_watched'] == False + assert response.data['total_watchers'] == 0 + assert response.data['is_watcher'] == False From 5c8b3ceabc15d130feffda83b2f9a62e5ba04c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 10 Oct 2015 14:11:32 +0200 Subject: [PATCH 157/190] [i18n] Update locales --- taiga/locale/es/LC_MESSAGES/django.po | 8 +- taiga/locale/it/LC_MESSAGES/django.po | 9 +- taiga/locale/pt_BR/LC_MESSAGES/django.po | 9 +- taiga/locale/ru/LC_MESSAGES/django.po | 115 ++++++++++++++++++----- 4 files changed, 107 insertions(+), 34 deletions(-) diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 7ed50fe0..d779fa5c 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"PO-Revision-Date: 2015-09-26 17:40+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" @@ -863,7 +863,7 @@ msgstr "Siguiente URL" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" -msgstr "" +msgstr "clave secreta para cifrar los tokens de aplicación" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -1318,7 +1318,7 @@ msgstr "No tienes suficientes permisos para ver esto." #: taiga/projects/attachments/api.py:47 msgid "Partial updates are not supported" -msgstr "" +msgstr "La actualización parcial no está soportada." #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1369,7 +1369,7 @@ msgstr "archivo adjunto" #: taiga/projects/attachments/models.py:71 msgid "sha1" -msgstr "" +msgstr "sha1" #: taiga/projects/attachments/models.py:73 msgid "is deprecated" diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index b0eb830b..7ce2d8bb 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -4,6 +4,7 @@ # # Translators: # Andrea Raimondi , 2015 +# David Barragán , 2015 # luca corsato , 2015 # Marco Somma , 2015 # Marco Vito Moscaritolo , 2015 @@ -13,7 +14,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"PO-Revision-Date: 2015-09-26 17:40+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/it/)\n" @@ -932,7 +933,7 @@ msgstr "Url successivo" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" -msgstr "" +msgstr "chiave segreta per cifrare i token dell'applicazione" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -1413,7 +1414,7 @@ msgstr "Non hai il permesso di vedere questo elemento." #: taiga/projects/attachments/api.py:47 msgid "Partial updates are not supported" -msgstr "" +msgstr "Aggiornamento non parziale non supportato" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1464,7 +1465,7 @@ msgstr "file allegato" #: taiga/projects/attachments/models.py:71 msgid "sha1" -msgstr "" +msgstr "sha1" #: taiga/projects/attachments/models.py:73 msgid "is deprecated" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index d6b64317..23e8f88e 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -6,6 +6,7 @@ # Cléber Zavadniak , 2015 # Thiago , 2015 # Daniel Dias , 2015 +# David Barragán , 2015 # Hevertton Barbosa , 2015 # Kemel Zaidan , 2015 # Marlon Carvalho , 2015 @@ -17,7 +18,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"PO-Revision-Date: 2015-09-26 17:38+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" @@ -869,7 +870,7 @@ msgstr "Próxima url" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" -msgstr "" +msgstr "clave secreta para cifrar los tokens de aplicación" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -1326,7 +1327,7 @@ msgstr "Você não tem permissão para ver isso" #: taiga/projects/attachments/api.py:47 msgid "Partial updates are not supported" -msgstr "" +msgstr "Las actualizaciones parciales no están soportadas" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1377,7 +1378,7 @@ msgstr "Arquivo anexado" #: taiga/projects/attachments/models.py:71 msgid "sha1" -msgstr "" +msgstr "sha1" #: taiga/projects/attachments/models.py:73 msgid "is deprecated" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 01bb217d..dabfc39a 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -4,6 +4,7 @@ # # Translators: # dmitriy , 2015 +# Dmitriy Volkov , 2015 # Dmitry Lobanov , 2015 # Dmitry Vinokurov , 2015 # Марат , 2015 @@ -12,8 +13,8 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"PO-Revision-Date: 2015-09-29 13:52+0000\n" +"Last-Translator: Dmitriy Volkov \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" @@ -505,7 +506,7 @@ msgstr "Нам была нужна хотя бы одна роль" #: taiga/export_import/api.py:197 msgid "Needed dump file" -msgstr "Нужен свалочный файл" +msgstr "Необходим дамп-файл" #: taiga/export_import/api.py:204 msgid "Invalid dump format" @@ -612,13 +613,12 @@ msgid "" " " msgstr "" "\n" -"

Создан свалочный файл проекта

\n" +"

Создан дамп проекта

\n" "

Здравствуйте, %(user)s,

\n" -"

Ваш свалочный файл для проекта %(project)s успешно сгенерирован.\n" +"

Дамп проекта %(project)s успешно сгенерирован.

\n" "

Вы можете скачать его здесь:

\n" -" Скачать свалочный файл\n" +" Скачать " +"дамп\n" "

Этот файл будет удалён %(deletion_date)s.

\n" "

Команда Taiga

\n" " " @@ -642,8 +642,8 @@ msgstr "" "\n" "Здравствуйте, %(user)s,\n" "\n" -"Ваш свалочный файл для проекта %(project)s успешно сгенерирован. Вы можете " -"скачать его здесь:\n" +"Дамп для проекта %(project)s успешно сгенерирован. Вы можете скачать его " +"здесь:\n" "\n" "%(url)s\n" "\n" @@ -655,7 +655,7 @@ msgstr "" #: taiga/export_import/templates/emails/dump_project-subject.jinja:1 #, python-format msgid "[%(project)s] Your project dump has been generated" -msgstr "[%(project)s] Свалочный файл вашего проекта успешно сгенерирован" +msgstr "[%(project)s] Дамп вашего проекта успешно сгенерирован" #: taiga/export_import/templates/emails/export_error-body-html.jinja:4 #, python-format @@ -790,9 +790,9 @@ msgid "" " " msgstr "" "\n" -"

Свалочный файл проекта импортирован

\n" +"

Дамп проекта импортирован

\n" "

Здравствуйте, %(user)s,

\n" -"

Свалочный файл вашего проекта успешно импортирован.

\n" +"

Дамп вашего проекта успешно импортирован.

\n" " Перейти к %(project)s\n" "

Команда Taiga

\n" @@ -816,7 +816,7 @@ msgstr "" "\n" "Здравствуйте, %(user)s,\n" "\n" -"Свалочный файл вашего проекта успешно импортирован.\n" +"Дамп вашего проекта успешно импортирован.\n" "\n" "Вы можете посмотреть %(project)s здесь:\n" "\n" @@ -828,12 +828,12 @@ msgstr "" #: taiga/export_import/templates/emails/load_dump-subject.jinja:1 #, python-format msgid "[%(project)s] Your project dump has been imported" -msgstr "[%(project)s] Свалочный файл вашего проекта импортирован" +msgstr "[%(project)s] Дамп вашего проекта импортирован" #: taiga/external_apps/api.py:40 taiga/external_apps/api.py:66 #: taiga/external_apps/api.py:73 msgid "Authentication required" -msgstr "" +msgstr "Необходима аутентификация" #: taiga/external_apps/models.py:33 #: taiga/projects/custom_attributes/models.py:36 @@ -848,7 +848,7 @@ msgstr "имя" #: taiga/external_apps/models.py:35 msgid "Icon url" -msgstr "" +msgstr "url иконки" #: taiga/external_apps/models.py:36 msgid "web" @@ -865,11 +865,11 @@ msgstr "описание" #: taiga/external_apps/models.py:39 msgid "Next url" -msgstr "" +msgstr "Следующий url" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" -msgstr "" +msgstr "секретный ключ для шифрования токенов приложения" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -878,7 +878,7 @@ msgstr "пользователь" #: taiga/external_apps/models.py:59 msgid "application" -msgstr "" +msgstr "приложение" #: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" @@ -1328,7 +1328,7 @@ msgstr "У вас нет разрешения на просмотр." #: taiga/projects/attachments/api.py:47 msgid "Partial updates are not supported" -msgstr "" +msgstr "Частичные обновления не поддерживаются" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" @@ -1379,7 +1379,7 @@ msgstr "приложенный файл" #: taiga/projects/attachments/models.py:71 msgid "sha1" -msgstr "" +msgstr "sha1" #: taiga/projects/attachments/models.py:73 msgid "is deprecated" @@ -3379,6 +3379,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Смена email

\n" +"

Здравствуйте, %(full_name)s,
подтвердите, пожалуйста, свой " +"email

\n" +" Подтвердить email\n" +"

Если вы не запрашивали смену email, не обращайте внимания на это " +"письмо.

\n" +"

Команда Taiga

\n" +" " #: taiga/users/templates/emails/change_email-body-text.jinja:1 #, python-format @@ -3393,6 +3403,15 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Здравствуйте, %(full_name)s, подтвердите, пожалуйста, свой email\n" +"\n" +"%(url)s\n" +"\n" +"Вы можете проигнорировать это сообщение, если не указывали этот email.\n" +"\n" +"---\n" +"The Taiga Team\n" #: taiga/users/templates/emails/change_email-subject.jinja:1 msgid "[Taiga] Change email" @@ -3410,6 +3429,16 @@ msgid "" "

The Taiga Team

\n" " " msgstr "" +"\n" +"

Восстановление пароля

\n" +"

Здравствуйте, %(full_name)s,
вы запросили восстановление " +"пароля

\n" +" Восстановить пароль\n" +"

Вы можете проигнорировать это сообщение, если не запрашивали " +"восстановление пароля.

\n" +"

Команда Taiga

\n" +" " #: taiga/users/templates/emails/password_recovery-body-text.jinja:1 #, python-format @@ -3424,6 +3453,16 @@ msgid "" "---\n" "The Taiga Team\n" msgstr "" +"\n" +"Здравствуйте, %(full_name)s, вы запросили восстановление пароля\n" +"\n" +"%(url)s\n" +"\n" +"Вы можете проигнорировать это сообщение, если не запрашивали восстановление " +"пароля.\n" +"\n" +"---\n" +"Команда Taiga\n" #: taiga/users/templates/emails/password_recovery-subject.jinja:1 msgid "[Taiga] Password recovery" @@ -3444,6 +3483,18 @@ msgid "" " \n" " " msgstr "" +"\n" +" \n" +"

Благодарим вас за регистрацию в Taiga

\n" +"

Мы надеемся, что вам понравится ей пользоваться

\n" +"

Мы сделали Taiga, потому что нам хотелось, чтобы программа " +"для управления проектами, которая весь день открыта у нас на компьютерах, " +"постоянно напоминала нам, почему нам нравиться работать вместе, " +"проектировать и программировать.

\n" +"

Мы сделали её красивой, элегантной, простой в использовании и " +"приятной - не жертвуя при этом гибкостью и возможностями.

\n" +" Команда Taiga\n" +" " #: taiga/users/templates/emails/registered_user-body-html.jinja:23 #, python-format @@ -3454,6 +3505,11 @@ msgid "" "here\n" " " msgstr "" +"\n" +" Вы можете удалить свой аккаунт посредством клика сюда\n" +" " #: taiga/users/templates/emails/registered_user-body-text.jinja:1 msgid "" @@ -3472,6 +3528,21 @@ msgid "" "--\n" "The taiga Team\n" msgstr "" +"\n" +"Благодарим вас за регистрацию в Taiga\n" +"\n" +"Мы надеемся, что вам понравится ей пользоваться\n" +"\n" +"Мы сделали Taiga, потому что нам хотелось, чтобы программа для управления " +"проектами, которая весь день открыта у нас на компьютерах, постоянно " +"напоминала нам, почему нам нравиться работать вместе, проектировать и " +"программировать.\n" +"\n" +"Мы сделали её красивой, элегантной, простой в использовании и приятной - не " +"жертвуя при этом гибкостью и возможностями.\n" +"\n" +"--\n" +"Команда Taiga\n" #: taiga/users/templates/emails/registered_user-body-text.jinja:13 #, python-format From 368df3709d4b2db6e8e585ff4567dabedbfcb3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 12 Oct 2015 10:32:13 +0200 Subject: [PATCH 158/190] [i18n] Update locales --- taiga/locale/pl/LC_MESSAGES/django.po | 9 +- taiga/locale/pt_BR/LC_MESSAGES/django.po | 299 +++++++++++------------ 2 files changed, 152 insertions(+), 156 deletions(-) diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index ff7d8e5d..330d4285 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -3,6 +3,7 @@ # This file is distributed under the same license as the taiga-back package. # # Translators: +# David Barragán , 2015 # Wiktor Żurawik , 2015 # Wojtek Jurkowlaniec , 2015 msgid "" @@ -10,7 +11,7 @@ msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" +"PO-Revision-Date: 2015-10-12 08:24+0000\n" "Last-Translator: David Barragán \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" @@ -847,7 +848,7 @@ msgstr "" #: taiga/external_apps/models.py:36 msgid "web" -msgstr "" +msgstr "web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:37 @@ -860,7 +861,7 @@ msgstr "opis" #: taiga/external_apps/models.py:39 msgid "Next url" -msgstr "" +msgstr "Następny url" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" @@ -873,7 +874,7 @@ msgstr "użytkownik" #: taiga/external_apps/models.py:59 msgid "application" -msgstr "" +msgstr "aplikacja" #: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 23e8f88e..1f252bea 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -13,13 +13,14 @@ # pedromvm , 2015 # Renato Prado , 2015 # Thiago , 2015 +# Walker de Alencar , 2015 msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:38+0000\n" -"Last-Translator: David Barragán \n" +"PO-Revision-Date: 2015-10-11 01:08+0000\n" +"Last-Translator: Walker de Alencar \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -222,11 +223,11 @@ msgstr "Objeto com %s=%s não existe." #: taiga/base/api/relations.py:346 msgid "Invalid hyperlink - No URL match" -msgstr "Hyperlink inválido - Nenhuma URL casa" +msgstr "Hyperlink inválido - Nenhuma URL corresponde" #: taiga/base/api/relations.py:347 msgid "Invalid hyperlink - Incorrect URL match" -msgstr "Hyperlink inválido - Casamento de URL incorreto" +msgstr "Hyperlink inválido - Corresponde a URL incorreta" #: taiga/base/api/relations.py:348 msgid "Invalid hyperlink due to configuration error" @@ -252,7 +253,7 @@ msgstr "Nenhuma entrada providenciada" #: taiga/base/api/serializers.py:548 msgid "Cannot create a new item, only existing items may be updated." msgstr "" -"Não é possível criar um novo item. Somente itens já existentes podem ser " +"Não é possível criar um novo item, somente itens já existentes podem ser " "atualizados." #: taiga/base/api/serializers.py:559 @@ -427,8 +428,8 @@ msgstr "" " %(support_url)s\n" "
\n" -" Nos comunique:\n" +" Entre em contato:" +"\n" " \n" " %(support_email)s\n" @@ -445,7 +446,7 @@ msgstr "" #: taiga/base/templates/emails/hero-body-html.jinja:6 msgid "You have been Taigatized" -msgstr "Você fpoi Taigueado" +msgstr "Você foi Taigatizado" #: taiga/base/templates/emails/hero-body-html.jinja:359 msgid "" @@ -457,7 +458,7 @@ msgid "" " " msgstr "" "\n" -"

Você foi taigueado!Você foi taigatizado!\n" "

Bem vindo ao Taiga, " "ferramenta de gerenciamento de projeto ágil e Open Source

\n" @@ -505,11 +506,11 @@ msgstr "Nós precisamos de pelo menos uma função" #: taiga/export_import/api.py:197 msgid "Needed dump file" -msgstr "Preciso de arquivo de restauração" +msgstr "Necessário de arquivo de restauração" #: taiga/export_import/api.py:204 msgid "Invalid dump format" -msgstr "formato de aquivo de restauração inválido" +msgstr "Formato de aquivo de restauração inválido" #: taiga/export_import/dump_service.py:96 msgid "error importing project data" @@ -619,7 +620,7 @@ msgstr "" "

Você pode baixa-lo aqui:

\n" "
Download do arquivo de restauração\n" -"

Esse arquivo será deletado em %(deletion_date)s.

\n" +"

Esse arquivo será apagado em %(deletion_date)s.

\n" "

O time Taiga

\n" " " @@ -647,7 +648,7 @@ msgstr "" "\n" "%(url)s\n" "\n" -"Esse arquivo será deletado em %(deletion_date)s.\n" +"Esse arquivo será apagado em %(deletion_date)s.\n" "\n" "---\n" "O time Taiga\n" @@ -870,7 +871,7 @@ msgstr "Próxima url" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" -msgstr "clave secreta para cifrar los tokens de aplicación" +msgstr "chave secreta para cifrar os tokens da aplicação" #: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 #: taiga/projects/votes/models.py:50 @@ -966,7 +967,7 @@ msgstr "" #: taiga/hooks/api.py:52 msgid "The payload is not a valid json" -msgstr "O carregamento não é um json válido" +msgstr "A carga não é um json válido" #: taiga/hooks/api.py:61 taiga/projects/issues/api.py:140 #: taiga/projects/tasks/api.py:84 taiga/projects/userstories/api.py:109 @@ -975,7 +976,7 @@ msgstr "O projeto não existe" #: taiga/hooks/api.py:64 msgid "Bad signature" -msgstr "Bad signature" +msgstr "Assinatura Ruim" #: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 @@ -1147,7 +1148,7 @@ msgid "" "\n" "{message}" msgstr "" -"Comentado pelo GitLab:\n" +"Comentário pelo GitLab:\n" "\n" "{message}" @@ -1231,11 +1232,11 @@ msgstr "Adicionar marco de progresso" #: taiga/permissions/permissions.py:55 msgid "Modify milestone" -msgstr "modificar marco de progresso" +msgstr "Modificar marco de progresso" #: taiga/permissions/permissions.py:56 msgid "Delete milestone" -msgstr "remover marco de progresso" +msgstr "Remover marco de progresso" #: taiga/permissions/permissions.py:58 msgid "View user story" @@ -1291,7 +1292,7 @@ msgstr "Modificar projeto" #: taiga/permissions/permissions.py:86 msgid "Add member" -msgstr "adicionar membro" +msgstr "Adicionar membro" #: taiga/permissions/permissions.py:87 msgid "Remove member" @@ -1327,11 +1328,11 @@ msgstr "Você não tem permissão para ver isso" #: taiga/projects/attachments/api.py:47 msgid "Partial updates are not supported" -msgstr "Las actualizaciones parciales no están soportadas" +msgstr "Atualizações parciais não são suportadas" #: taiga/projects/attachments/api.py:62 msgid "Project ID not matches between object and project" -msgstr "Project ID não encontrado entre objeto e projeto" +msgstr "ID do projeto não combina entre objeto e projeto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 @@ -1339,7 +1340,7 @@ msgstr "Project ID não encontrado entre objeto e projeto" #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 #: taiga/userstorage/models.py:25 msgid "owner" -msgstr "Dono" +msgstr "dono" #: taiga/projects/attachments/models.py:54 #: taiga/projects/custom_attributes/models.py:41 @@ -1357,7 +1358,7 @@ msgstr "projeto" #: taiga/projects/attachments/models.py:56 msgid "content type" -msgstr "tipo de conteudo" +msgstr "tipo de conteúdo" #: taiga/projects/attachments/models.py:58 msgid "object id" @@ -1374,7 +1375,7 @@ msgstr "data modificação" #: taiga/projects/attachments/models.py:69 msgid "attached file" -msgstr "Arquivo anexado" +msgstr "arquivo anexado" #: taiga/projects/attachments/models.py:71 msgid "sha1" @@ -1396,7 +1397,7 @@ msgstr "ordem" #: taiga/projects/choices.py:21 msgid "AppearIn" -msgstr "AppearIn" +msgstr "Aparece em" #: taiga/projects/choices.py:22 msgid "Jitsi" @@ -1404,7 +1405,7 @@ msgstr "Jitsi" #: taiga/projects/choices.py:23 msgid "Custom" -msgstr "personalizado" +msgstr "Personalizado" #: taiga/projects/choices.py:24 msgid "Talky" @@ -1622,17 +1623,17 @@ msgstr "marco de progresso" #: taiga/projects/issues/models.py:58 taiga/projects/tasks/models.py:51 msgid "finished date" -msgstr "Data de término" +msgstr "data de término" #: taiga/projects/issues/models.py:60 taiga/projects/tasks/models.py:53 #: taiga/projects/userstories/models.py:89 msgid "subject" -msgstr "Assunto" +msgstr "assunto" #: taiga/projects/issues/models.py:64 taiga/projects/tasks/models.py:63 #: taiga/projects/userstories/models.py:93 msgid "assigned to" -msgstr "Assinado a" +msgstr "assinado a" #: taiga/projects/issues/models.py:66 taiga/projects/tasks/models.py:67 #: taiga/projects/userstories/models.py:103 @@ -1648,7 +1649,7 @@ msgstr "slug" #: taiga/projects/milestones/models.py:42 msgid "estimated start date" -msgstr "Data de início estimada" +msgstr "data de início estimada" #: taiga/projects/milestones/models.py:43 msgid "estimated finish date" @@ -1738,7 +1739,7 @@ msgstr "tipo padrão de caso" #: taiga/projects/models.py:147 msgid "members" -msgstr "members" +msgstr "membros" #: taiga/projects/models.py:150 msgid "total of milestones" @@ -1746,7 +1747,7 @@ msgstr "total de marcos de progresso" #: taiga/projects/models.py:151 msgid "total story points" -msgstr "pontos totáis de US" +msgstr "pontos totais de US" #: taiga/projects/models.py:154 taiga/projects/models.py:614 msgid "active backlog panel" @@ -1766,11 +1767,11 @@ msgstr "painel de casos ativo" #: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" -msgstr "sistema de videoconferencia" +msgstr "sistema de vídeo conferência" #: taiga/projects/models.py:165 taiga/projects/models.py:625 msgid "videoconference extra data" -msgstr "informação extra de videoconferencia" +msgstr "informação extra de vídeo conferência" #: taiga/projects/models.py:170 msgid "creation template" @@ -1794,7 +1795,7 @@ msgstr "cores de tags" #: taiga/projects/models.py:383 msgid "modules config" -msgstr "configurações de modulos" +msgstr "configurações de módulos" #: taiga/projects/models.py:402 msgid "is archived" @@ -1858,11 +1859,11 @@ msgstr "funções" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "não acompanhando" +msgstr "não observando" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "acompanhando" +msgstr "observando" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" @@ -1892,7 +1893,7 @@ msgstr "Observado" #: taiga/projects/notifications/services.py:66 #: taiga/projects/notifications/services.py:80 msgid "Notify exists for specified user and project" -msgstr "notificação exist para usuário específico e projeto" +msgstr "Existe notificação para usuário e projeto especifcado" #: taiga/projects/notifications/services.py:428 msgid "Invalid value for notify level" @@ -1911,10 +1912,10 @@ msgid "" " " msgstr "" "\n" -"

caso atualizado

\n" -"

olá %(user)s,
%(changer)s atualizou caso em %(project)s

\n" -"

caso #%(ref)s %(subject)s

\n" -" Caso atualizado\n" +"

Olá %(user)s,
%(changer)s atualizou caso em %(project)s

\n" +"

Caso #%(ref)s %(subject)s

\n" +"
Ver caso\n" "\n" " " @@ -1939,7 +1940,7 @@ msgid "" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Atualizou um caso #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Atualizou o caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 #, python-format @@ -1955,12 +1956,11 @@ msgid "" " " msgstr "" "\n" -"

New issue created

\n" -"

Hello %(user)s,
%(changer)s has created a new issue on " -"%(project)s

\n" -"

Issue #%(ref)s %(subject)s

\n" -" See issue\n" +"

Novo caso criado

\n" +"

Olá %(user)s,
%(changer)s criou um novo caso em %(project)s

\n" +"

Caso #%(ref)s %(subject)s

\n" +" Ver caso\n" "

O Time Taiga

\n" " " @@ -1977,7 +1977,7 @@ msgid "" msgstr "" "\n" "Novo caso criado\n" -"Olá %(user)s, %(changer)s criaram um novo caso em %(project)s\n" +"Olá %(user)s, %(changer)s criou um novo caso em %(project)s\n" "Ver caso #%(ref)s %(subject)s em %(url)s\n" "\n" "---\n" @@ -1990,7 +1990,7 @@ msgid "" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Criou um caso #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Criou o caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 #, python-format @@ -2004,8 +2004,8 @@ msgid "" " " msgstr "" "\n" -"

Caso deletado

\n" -"

Olá %(user)s,
%(changer)s deletaram um caso em %(project)s

\n" +"

Caso apagado

\n" +"

Olá %(user)s,
%(changer)s apagou um caso em %(project)s

\n" "

Caso #%(ref)s %(subject)s

\n" "

O Time Taiga

\n" " " @@ -2022,8 +2022,8 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Caso deletado\n" -"Olá %(user)s, %(changer)s deletaram um caso em %(project)s\n" +"Caso apagado\n" +"Olá %(user)s, %(changer)s apagou um caso em %(project)s\n" "caso #%(ref)s %(subject)s\n" "\n" "---\n" @@ -2036,7 +2036,7 @@ msgid "" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Removeu o caso #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Apagou o caso #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 #, python-format @@ -2096,8 +2096,7 @@ msgid "" msgstr "" "\n" "

Novo sprint criado

\n" -"

Olá %(user)s,
%(changer)s criaram novo sprint em %(project)s\n" +"

Olá %(user)s,
%(changer)s criou novo sprint em %(project)s

\n" "

Sprint %(name)s

\n" " Ver " "sprint\n" @@ -2144,8 +2143,8 @@ msgid "" " " msgstr "" "\n" -"

Sprint deletado

\n" -"

Olá %(user)s,
%(changer)s deletou sprint em %(project)s

\n" +"

Sprint apagado

\n" +"

Olá %(user)s,
%(changer)s apagou sprint em %(project)s

\n" "

Sprint %(name)s

\n" "

O Time Taiga

\n" " " @@ -2163,7 +2162,7 @@ msgid "" msgstr "" "\n" "Sprint deletado\n" -"Olá %(user)s, %(changer)s deletaram sprint em %(project)s\n" +"Olá %(user)s, %(changer)s apagado sprint em %(project)s\n" "Sprint %(name)s\n" "\n" "---\n" @@ -2176,7 +2175,7 @@ msgid "" "[%(project)s] Deleted the Sprint \"%(milestone)s\"\n" msgstr "" "\n" -"[%(project)s] Removeu o Sprint \"%(milestone)s\"\n" +"[%(project)s] Apagou o Sprint \"%(milestone)s\"\n" #: taiga/projects/notifications/templates/emails/tasks/task-change-body-html.jinja:4 #, python-format @@ -2282,8 +2281,8 @@ msgid "" " " msgstr "" "\n" -"

Tarefa deletada

\n" -"

Olá %(user)s,
%(changer)sdeletaram tarefa em %(project)s

\n" +"

Tarefa apagada

\n" +"

Olá %(user)s,
%(changer)s apagou uma tarefa em %(project)s

\n" "

Tarefa #%(ref)s %(subject)s

\n" "

O Time Taiga

\n" " " @@ -2300,8 +2299,8 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Tarefa deletada\n" -"Olá %(user)s, %(changer)s deletou tarefa em %(project)s\n" +"Tarefa apagada \n" +"Olá %(user)s, %(changer)s apagou tarefa em %(project)s\n" "Tarefa #%(ref)s %(subject)s\n" "\n" "---\n" @@ -2314,7 +2313,7 @@ msgid "" "[%(project)s] Deleted the task #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Deletou a tarefa #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Apagou a tarefa #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/userstories/userstory-change-body-html.jinja:4 #, python-format @@ -2422,9 +2421,9 @@ msgid "" " " msgstr "" "\n" -"

User Story deleted

\n" -"

Hello %(user)s,
%(changer)s has deleted a user story on " -"%(project)s

\n" +"

User Story apagada

\n" +"

Olá %(user)s,
%(changer)s apagou uma user story em %(project)s\n" "

User Story #%(ref)s %(subject)s

\n" "

O Time Taiga

\n" " " @@ -2441,8 +2440,8 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"User Story deletada\n" -"Olá %(user)s, %(changer)s deletou user story em %(project)s\n" +"User Story apagada\n" +"Olá %(user)s, %(changer)s apagou user story em %(project)s\n" "User Story #%(ref)s %(subject)s\n" "\n" "---\n" @@ -2455,7 +2454,7 @@ msgid "" "[%(project)s] Deleted the US #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Removeu a US #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Apagou a US #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-body-html.jinja:4 #, python-format @@ -2470,7 +2469,7 @@ msgid "" " " msgstr "" "\n" -"

Página Wiki modificada

\n" +"

Página Wiki atualizada

\n" "

Olá %(user)s,
%(changer)s atualizou a página wiki em%(project)s\n" "

Página Wiki %(page)s

\n" @@ -2489,7 +2488,7 @@ msgid "" "See wiki page %(page)s at %(url)s\n" msgstr "" "\n" -"Página Wiki modificada\n" +"Página Wiki atualizada\n" "\n" "Olá %(user)s, %(changer)s atualizou a página wiki em %(project)s\n" "\n" @@ -2502,7 +2501,7 @@ msgid "" "[%(project)s] Updated the Wiki Page \"%(page)s\"\n" msgstr "" "\n" -"[%(project)s] atualizou a página wiki \"%(page)s\"\n" +"[%(project)s] Atualizou a página wiki \"%(page)s\"\n" #: taiga/projects/notifications/templates/emails/wiki/wikipage-create-body-html.jinja:4 #, python-format @@ -2518,12 +2517,12 @@ msgid "" " " msgstr "" "\n" -"

New wiki page created

\n" -"

Hello %(user)s,
%(changer)s has created a new wiki page on " +"

Nova página wiki criada

\n" +"

Olá %(user)s,
%(changer)s criou uma nova página wiki em " "%(project)s

\n" -"

Wiki page %(page)s

\n" -" See " -"wiki page\n" +"

Página wiki %(page)s

\n" +" Ver " +"página wiki\n" "

O Time Taiga

\n" " " @@ -2571,10 +2570,10 @@ msgid "" " " msgstr "" "\n" -"

Wiki page deleted

\n" -"

Hello %(user)s,
%(changer)s has deleted a wiki page on " -"%(project)s

\n" -"

Wiki page %(page)s

\n" +"

Página Wiki apagada

\n" +"

Olá %(user)s,
%(changer)s apagou uma página wiki em %(project)s\n" +"

Página Wiki %(page)s

\n" "

O Time Taiga

\n" " " @@ -2592,9 +2591,9 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Página wiki deletada\n" +"Página wiki apagada\n" "\n" -"Olá %(user)s, %(changer)s deletou página wiki em %(project)s\n" +"Olá %(user)s, %(changer)s apagou uma página wiki em %(project)s\n" "\n" "Página Wiki %(page)s\n" "\n" @@ -2608,11 +2607,11 @@ msgid "" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" msgstr "" "\n" -"[%(project)s] Removeu a página Wiki \"%(page)s\"\n" +"[%(project)s] Apagou a página Wiki \"%(page)s\"\n" #: taiga/projects/notifications/validators.py:45 msgid "Watchers contains invalid users" -msgstr "Visualizadores contém usuário inválido" +msgstr "Observadores contém usuários inválidos" #: taiga/projects/occ/mixins.py:35 msgid "The version must be an integer" @@ -2620,11 +2619,11 @@ msgstr "A versão precisa ser um inteiro" #: taiga/projects/occ/mixins.py:58 msgid "The version parameter is not valid" -msgstr "A versão do parâmetro não é válida" +msgstr "O parâmetro da versão não é válido" #: taiga/projects/occ/mixins.py:74 msgid "The version doesn't match with the current one" -msgstr "A versão não verifica com a atual" +msgstr "A versão não corresponde com a atual" #: taiga/projects/occ/mixins.py:93 msgid "version" @@ -2648,7 +2647,7 @@ msgstr "Opções padrão" #: taiga/projects/serializers.py:400 msgid "User story's statuses" -msgstr "Status de US" +msgstr "Status de user story" #: taiga/projects/serializers.py:401 msgid "Points" @@ -2700,11 +2699,11 @@ msgstr "Você não tem permissão para colocar esse status para essa tarefa." #: taiga/projects/tasks/models.py:56 msgid "us order" -msgstr "ordem US" +msgstr "ordenar por US" #: taiga/projects/tasks/models.py:58 msgid "taskboard order" -msgstr "ordem de quadro de tarefa" +msgstr "ordenar por quadro de tarefa" #: taiga/projects/tasks/models.py:66 msgid "is iocaine" @@ -2730,7 +2729,7 @@ msgid "" " " msgstr "" "\n" -"

Você foi convidado para o Taiga!

\n" +"

Você foi convidado para o Taiga!

\n" "

Oi! %(full_name)s te enviou um convite para se juntar ao projeto " "%(project)s no Taiga.
Taiga é uma ferramenta de gerenciamento de " "projetos ágil, código aberto e grátis.

\n" @@ -2746,10 +2745,9 @@ msgid "" " " msgstr "" "\n" -"

E agora algumas palavrãos do bom companheiros ou " -"companheiras
que vieram tão gentilmente convidá-lo

\n" -"

%(extra)s

\n" -" " +"

E agora algumas palavras do bom companheiros ou companheiras
" +"que vieram tão gentilmente convidá-lo

\n" +"

%(extra)s

" #: taiga/projects/templates/emails/membership_invitation-body-html.jinja:24 msgid "Accept your invitation to Taiga" @@ -2774,11 +2772,11 @@ msgid "" "Project Management Tool.\n" msgstr "" "\n" -"Você ou algum conhecido te convidou para o Taiga\n" +"Você, ou algum conhecido, convidou para o Taiga\n" "\n" "Oi! %(full_name)s te enviou um convite para se juntar ao projeto chamado " -"%(project)s que está começando a ser gerenciado no Taiga, um gratuíto, " -"ferramenta de gerenciamento de projetos ágeis de código aberto.\n" +"%(project)s que está começando a ser gerenciado no Taiga, Taiga é uma " +"ferramenta de gerenciamento de projetos ágil, código aberto e grátis.\n" #: taiga/projects/templates/emails/membership_invitation-body-text.jinja:12 #, python-format @@ -2791,8 +2789,8 @@ msgid "" " " msgstr "" "\n" -"E agora algumas palavras do bom companheiro ou companheiras que pensavam " -"como tão gentilmente como poderiam convidá-lo:\n" +"E agora algumas palavras do bom companheiro ou companheira que pensou tão " +"gentilmente como convidá-lo:\n" "\n" "%(extra)s\n" " " @@ -2833,12 +2831,11 @@ msgid "" " " msgstr "" "\n" -"

Você foi adicionada ao projeto

\n" -"

Olá %(full_name)s,
você foi adicionado ao projeto %(project)s\n" -" Ir ao " +"

Você foi adicionado ao projeto

\n" +"

Olá %(full_name)s,
você foi adicionado ao projeto %(project)s

\n" +"
Ir ao " "projeto\n" -"

O Time Taiga

\n" +"

O Time Taiga

\n" " " #: taiga/projects/templates/emails/membership_notification-body-text.jinja:1 @@ -2852,7 +2849,7 @@ msgid "" msgstr "" "\n" "Você foi adicionado ao projeto\n" -"Olá %(full_name)s,você foi adicionado ao projeto %(project)s\n" +"Olá %(full_name)s, você foi adicionado ao projeto %(project)s\n" "\n" "Ver projeto em %(url)s\n" @@ -2882,10 +2879,9 @@ msgid "" msgstr "" "O backlog no scrum é uma lista de funcionalidades priorizadas, contendo " "pequenas descrições de todas as funcionalidades desejadas no produto. Quando " -"se aplicada ao scrum, não é necessário começar com um longo, esforço " -"inicial para documentar todos os requisitos. O backlog é então permitido " -"crescer e modificar-se no processo que é compreendido sobre o produto e seus " -"consumidores." +"se aplicada ao scrum, não é necessário começar com um longo esforço inicial " +"para documentar todos os requisitos. O backlog permite crescer e modificar-" +"se no processo que é compreendido sobre o produto e seus clientes." #. Translators: Name of kanban project template. #: taiga/projects/translations.py:33 @@ -2900,10 +2896,11 @@ msgid "" "process, from definition of a task to its delivery to the customer, is " "displayed for participants to see and team members pull work from a queue." msgstr "" -"Kanban é um método de gerenciamento intelectual com ênfase em entregas just-" -"in-time, não sobrecarregando membros dos times. Nessa abordagem, o processo, " -"da definição da tarefa a entrega para o consumidor, os participantes podem " -"visualizar os próprios membros do time pegar o trabalho de uma lista." +"Kanban é um método de gerenciar o trabalho conhecido com ênfase em entregas " +"just-in-time, não sobrecarregando membros dos times. Nessa abordagem, o " +"processo, da definição da tarefa até a entrega para o cliente, é exibida " +"para os participantes verem os próprios membros do time pegar o trabalho de " +"uma lista." #. Translators: User story point value (value = undefined) #: taiga/projects/translations.py:43 @@ -2976,7 +2973,7 @@ msgstr "Novo" #. Translators: User story status #: taiga/projects/translations.py:76 msgid "Ready" -msgstr "Acabado" +msgstr "Pronto" #. Translators: User story status #. Translators: Task status @@ -3143,15 +3140,15 @@ msgstr "data de término" #: taiga/projects/userstories/models.py:95 msgid "is client requirement" -msgstr "É requerimento de cliente" +msgstr "É requerimento do cliente" #: taiga/projects/userstories/models.py:97 msgid "is team requirement" -msgstr "É requerimento de time" +msgstr "É requerimento do time" #: taiga/projects/userstories/models.py:102 msgid "generated from issue" -msgstr "Gerado a partir de caso" +msgstr "Gerado do caso" #: taiga/projects/userstories/validators.py:28 msgid "There's no user story with that id" @@ -3163,11 +3160,11 @@ msgstr "Não há projeto com esse id" #: taiga/projects/validators.py:37 msgid "There's no user story status with that id" -msgstr "Não há status de user story com aquele id" +msgstr "Não há status de user story com este id" #: taiga/projects/validators.py:46 msgid "There's no task status with that id" -msgstr "Não há status de tarega com aquele id" +msgstr "Não há status de tarega com este id" #: taiga/projects/votes/models.py:28 msgid "count" @@ -3216,7 +3213,7 @@ msgstr "Datas importantes" #: taiga/users/api.py:150 taiga/users/api.py:157 msgid "Invalid username or email" -msgstr "usuário ou e-mail inválido" +msgstr "Usuário ou e-mail inválido" #: taiga/users/api.py:166 msgid "Mail sended successful!" @@ -3228,7 +3225,7 @@ msgstr "Token é inválido" #: taiga/users/api.py:204 msgid "Current password parameter needed" -msgstr "parâmetro de senha atual necessário" +msgstr "Parâmetro de senha atual necessário" #: taiga/users/api.py:207 msgid "New password parameter needed" @@ -3240,19 +3237,19 @@ msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" #: taiga/users/api.py:213 msgid "Invalid current password" -msgstr "senha atual inválida" +msgstr "Senha atual inválida" #: taiga/users/api.py:229 msgid "Incomplete arguments" -msgstr "argumentos incompletos" +msgstr "Argumentos incompletos" #: taiga/users/api.py:234 msgid "Invalid image format" -msgstr "formato de imagem inválida" +msgstr "Formato de imagem inválida" #: taiga/users/api.py:279 msgid "Duplicated email" -msgstr "e-mail duplicado" +msgstr "E-mail duplicado" #: taiga/users/api.py:281 msgid "Not valid email" @@ -3267,7 +3264,7 @@ msgstr "" #: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 msgid "Invalid, are you sure the token is correct?" -msgstr "Inválido, está certo que o token está correto?" +msgstr "Inválido, tem certeza que o token está correto?" #: taiga/users/models.py:71 msgid "superuser status" @@ -3419,13 +3416,12 @@ msgid "" " " msgstr "" "\n" -"

Recupere sua senha

\n" -"

Olá %(full_name)s,
você solicitou para recuperar sua senha\n" -" Recuperar sua senha\n" -"

Você pode ignorar essa mensagem se não solicitou.

\n" -"

O Time Taiga

\n" +"

Recuperar sua senha

\n" +"

Olá %(full_name)s,
você solicitou para recuperar sua senha

\n" +"Recuperar " +"sua senha\n" +"

Você pode ignorar essa mensagem se não solicitou.

\n" +"

O Time Taiga

\n" " " #: taiga/users/templates/emails/password_recovery-body-text.jinja:1 @@ -3471,18 +3467,17 @@ msgid "" " " msgstr "" "\n" -" \n" -"

Obrigado por se registrar no Taiga

\n" -"

Esperamos que você goste

\n" -"

Fizemos o taiga porque queriamos uma ferramenta de " -"gerenciamento de projetos que se colocasse aberta em nosso computadores " -"durante o dia, que nos lembrassem porque amamos colaborar, programar e " -"projetar.

\n" -"

Construimos para ser bela, elegante, simples de usar e " -"divertida - sem abrir mão de flexibilidade e poder.

\n" -" O Time Taiga\n" -" \n" -" " +"\n" +"

Obrigado por se registrar no Taiga

\n" +"

Esperamos que você goste

\n" +"

Fizemos o taiga porque queriamos uma ferramenta de gerenciamento de " +"projetos que se colocasse aberta em nosso computadores durante o dia, que " +"nos lembrassem porque amamos colaborar, programar e projetar.

\n" +"

Construimos para ser bela, elegante, simples de usar e divertida - sem " +"abrir mão de flexibilidade e poder.

\n" +"O Time Taiga\n" +"\n" +" " #: taiga/users/templates/emails/registered_user-body-html.jinja:23 #, python-format @@ -3542,7 +3537,7 @@ msgstr "" #: taiga/users/templates/emails/registered_user-subject.jinja:1 msgid "You've been Taigatized!" -msgstr "Você foi Taigueado!" +msgstr "Você foi Taigatizado!" #: taiga/users/validators.py:29 msgid "There's no role with that id" From 9226913caab94b2f5e2a761d8e56b613198a656d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 13 Oct 2015 11:04:40 +0200 Subject: [PATCH 159/190] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7969f798..37aca49a 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Managed with Taiga.io](https://taiga.io/media/support/attachments/article-22/banner-gh.png)](https://taiga.io "Managed with Taiga.io") [![Build Status](https://travis-ci.org/taigaio/taiga-back.svg?branch=master)](https://travis-ci.org/taigaio/taiga-back "Build Status") [![Coverage Status](https://coveralls.io/repos/taigaio/taiga-back/badge.svg?branch=master)](https://coveralls.io/r/taigaio/taiga-back?branch=master "Coverage Status") +[![Dependency Status](https://www.versioneye.com/user/projects/561bd091a193340f32001464/badge.svg?style=flat)](https://www.versioneye.com/user/projects/561bd091a193340f32001464) ## Setup development environment ## From a7a6bd3a1c85b56c9d8bff801b986c7801097f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 5 Oct 2015 15:41:17 +0200 Subject: [PATCH 160/190] Migrate to django 1.8 and make taiga compatible with python 3.5 --- .travis.yml | 1 + CHANGELOG.md | 3 + requirements-devel.txt | 14 +- requirements.txt | 54 ++- settings/common.py | 39 +- settings/development.py | 5 +- settings/local.py.example | 2 + taiga/auth/services.py | 6 +- taiga/base/api/serializers.py | 2 +- taiga/base/api/views.py | 2 +- taiga/base/mails.py | 42 ++ taiga/base/management/commands/test_emails.py | 32 +- taiga/base/neighbors.py | 2 +- taiga/base/utils/contenttypes.py | 23 + taiga/events/signal_handlers.py | 2 + taiga/export_import/tasks.py | 14 +- taiga/feedback/services.py | 5 +- taiga/front/templatetags/functions.py | 5 +- taiga/mdrender/service.py | 10 +- taiga/mdrender/templatetags/functions.py | 4 +- .../history/templatetags/functions.py | 7 +- .../migrations/0006_remove_issue_watchers.py | 2 +- .../management/commands/sample_data.py | 1 - .../0002_remove_milestone_watchers.py | 2 +- .../migrations/0005_auto_20151005_1357.py | 25 ++ taiga/projects/notifications/models.py | 4 +- taiga/projects/notifications/services.py | 5 +- taiga/projects/services/invitations.py | 7 +- .../migrations/0008_remove_task_watchers.py | 2 +- .../0010_remove_userstory_watchers.py | 2 +- .../0002_remove_wikipage_watchers.py | 2 +- taiga/users/api.py | 10 +- taiga/users/forms.py | 2 +- .../migrations/0014_auto_20151005_1357.py | 31 ++ tests/integration/test_notifications.py | 401 ++++++++++++++---- tests/integration/test_timeline.py | 1 - tests/integration/test_users.py | 3 - tests/integration/test_userstories.py | 2 +- tests/unit/test_export.py | 1 - tests/unit/test_mdrender.py | 4 +- 40 files changed, 563 insertions(+), 218 deletions(-) create mode 100644 taiga/base/mails.py create mode 100644 taiga/base/utils/contenttypes.py create mode 100644 taiga/projects/notifications/migrations/0005_auto_20151005_1357.py create mode 100644 taiga/users/migrations/0014_auto_20151005_1357.py diff --git a/.travis.yml b/.travis.yml index d0bb13ca..4a94cfaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ sudo: false language: python python: - "3.4" + - "3.5" services: - rabbitmq # will start rabbitmq-server cache: diff --git a/CHANGELOG.md b/CHANGELOG.md index 60a77eaf..465ef394 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,9 @@ ### Misc +- Made compatible with python 3.5. +- Migrated to django 1.8. +- Update the rest of requirements to the last version. - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) - API: Improve and fix some errors in issues/filters_data and userstories/filters_data. diff --git a/requirements-devel.txt b/requirements-devel.txt index df6cacac..bca12fc2 100644 --- a/requirements-devel.txt +++ b/requirements-devel.txt @@ -1,13 +1,13 @@ -r requirements.txt -factory_boy==2.4.1 -py==1.4.26 -pytest==2.6.4 -pytest-django==2.8.0 -pytest-pythonpath==0.6 +factory_boy==2.5.2 +py==1.4.30 +pytest==2.8.2 +pytest-django==2.9.1 +pytest-pythonpath==0.7 -coverage==3.7.1 -coveralls==0.4.2 +coverage==4.0 +coveralls==1.0 django-slowdown==0.0.1 transifex-client==0.11.1.beta diff --git a/requirements.txt b/requirements.txt index e116de5f..6d2df509 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,37 +1,35 @@ -Django==1.7.8 +Django==1.8.5 #djangorestframework==2.3.13 # It's not necessary since Taiga 1.7 -django-picklefield==0.3.1 -django-sampledatahelper==0.2.2 +django-picklefield==0.3.2 +django-sampledatahelper==0.3.0 gunicorn==19.3.0 -psycopg2==2.5.4 -pillow==2.5.3 -pytz==2014.4 -six==1.8.0 -amqp==1.4.6 +psycopg2==2.6.1 +Pillow==3.0.0 +pytz==2015.6 +six==1.10.0 +amqp==1.4.7 djmail==0.11 -django-pgjson==0.2.2 -djorm-pgarray==1.0.4 -django-jinja==1.0.4 -jinja2==2.7.2 -pygments==1.6 +django-pgjson==0.3.1 +djorm-pgarray==1.2 +django-jinja==1.4.1 +jinja2==2.8 +pygments==2.0.2 django-sites==0.8 -Markdown==2.4.1 -fn==0.2.13 +Markdown==2.6.2 +fn==0.4.3 diff-match-patch==20121119 -requests==2.4.1 +requests==2.8.0 django-sr==0.0.4 -easy-thumbnails==2.1 -celery==3.1.17 +easy-thumbnails==2.2 +celery==3.1.18 redis==2.10.3 -Unidecode==0.04.16 -raven==5.1.1 -bleach==1.4 -django-ipware==0.1.0 -premailer==2.8.1 +Unidecode==0.04.18 +raven==5.7.2 +bleach==1.4.2 +django-ipware==1.1.1 +premailer==2.9.6 +cssutils==1.0.1 # Compatible with python 3.5 django-transactional-cleanup==0.1.15 -lxml==3.4.1 +lxml==3.5.0b1 git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea -pyjwkest==1.0.3 - -# Comment it if you are using python >= 3.4 -enum34==1.0 +pyjwkest==1.0.5 diff --git a/settings/common.py b/settings/common.py index 9a256a45..37fb1e27 100644 --- a/settings/common.py +++ b/settings/common.py @@ -25,6 +25,8 @@ ADMINS = ( ("Admin", "example@example.com"), ) +DEBUG = False + DATABASES = { "default": { "ENGINE": "transaction_hooks.backends.postgresql_psycopg2", @@ -215,11 +217,29 @@ DEFAULT_FILE_STORAGE = "taiga.base.storage.FileSystemStorage" SECRET_KEY = "aw3+t2r(8(0kkrhg8)gx6i96v5^kv%6cfep9wxfom0%7dy0m9e" -TEMPLATE_LOADERS = [ - "django_jinja.loaders.AppLoader", - "django_jinja.loaders.FileSystemLoader", +TEMPLATES = [ + { + "BACKEND": "django_jinja.backend.Jinja2", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), + ], + "APP_DIRS": True, + "OPTIONS": { + 'context_processors': [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.request", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.contrib.messages.context_processors.messages", + ], + "match_extension": ".jinja", + } + }, ] + MIDDLEWARE_CLASSES = [ "taiga.base.middleware.cors.CoorsMiddleware", "taiga.events.middleware.SessionIDMiddleware", @@ -234,22 +254,9 @@ MIDDLEWARE_CLASSES = [ "django.contrib.messages.middleware.MessageMiddleware", ] -TEMPLATE_CONTEXT_PROCESSORS = [ - "django.contrib.auth.context_processors.auth", - "django.core.context_processors.request", - "django.core.context_processors.i18n", - "django.core.context_processors.media", - "django.core.context_processors.static", - "django.core.context_processors.tz", - "django.contrib.messages.context_processors.messages", -] ROOT_URLCONF = "taiga.urls" -TEMPLATE_DIRS = [ - os.path.join(BASE_DIR, "templates"), -] - INSTALLED_APPS = [ "django.contrib.auth", "django.contrib.contenttypes", diff --git a/settings/development.py b/settings/development.py index 008c128b..950ddd92 100644 --- a/settings/development.py +++ b/settings/development.py @@ -17,8 +17,5 @@ from .common import * DEBUG = True -TEMPLATE_DEBUG = DEBUG -TEMPLATE_CONTEXT_PROCESSORS += [ - "django.core.context_processors.debug", -] +TEMPLATES[0]["OPTIONS"]['context_processors'] += "django.template.context_processors.debug" diff --git a/settings/local.py.example b/settings/local.py.example index 95ce96fd..a6d16fab 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -16,6 +16,8 @@ from .development import * +#DEBUG = False + #ADMINS = ( # ("Admin", "example@example.com"), #) diff --git a/taiga/auth/services.py b/taiga/auth/services.py index 952bf6a7..55277a8f 100644 --- a/taiga/auth/services.py +++ b/taiga/auth/services.py @@ -28,9 +28,8 @@ from django.db import transaction as tx from django.db import IntegrityError from django.utils.translation import ugettext as _ -from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail - from taiga.base import exceptions as exc +from taiga.base.mails import mail_builder from taiga.users.serializers import UserAdminSerializer from taiga.users.services import get_and_validate_user @@ -57,8 +56,7 @@ def send_register_email(user) -> bool: """ cancel_token = get_token_for_user(user, "cancel_account") context = {"user": user, "cancel_token": cancel_token} - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - email = mbuilder.registered_user(user, context) + email = mail_builder.registered_user(user, context) return bool(email.send()) diff --git a/taiga/base/api/serializers.py b/taiga/base/api/serializers.py index 8add00ba..9a52e873 100644 --- a/taiga/base/api/serializers.py +++ b/taiga/base/api/serializers.py @@ -1005,7 +1005,7 @@ class ModelSerializer((six.with_metaclass(SerializerMetaclass, BaseSerializer))) m2m_data[field_name] = attrs.pop(field_name) # Forward m2m relations - for field in meta.many_to_many + meta.virtual_fields: + for field in list(meta.many_to_many) + meta.virtual_fields: if field.name in attrs: m2m_data[field.name] = attrs.pop(field.name) diff --git a/taiga/base/api/views.py b/taiga/base/api/views.py index 7a751eaf..293053da 100644 --- a/taiga/base/api/views.py +++ b/taiga/base/api/views.py @@ -447,7 +447,7 @@ class APIView(View): def api_server_error(request, *args, **kwargs): - if settings.DEBUG is False and request.META['CONTENT_TYPE'] == "application/json": + if settings.DEBUG is False and request.META.get('CONTENT_TYPE', None) == "application/json": return HttpResponse(json.dumps({"error": _("Server application error")}), status=status.HTTP_500_INTERNAL_SERVER_ERROR) return server_error(request, *args, **kwargs) diff --git a/taiga/base/mails.py b/taiga/base/mails.py new file mode 100644 index 00000000..b078cbcf --- /dev/null +++ b/taiga/base/mails.py @@ -0,0 +1,42 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 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 django.conf import settings + +from djmail import template_mail +import premailer + +import logging + + +# Hide CSS warnings messages if debug mode is disable +if not getattr(settings, "DEBUG", False): + premailer.premailer.cssutils.log.setLevel(logging.CRITICAL) + + +class InlineCSSTemplateMail(template_mail.TemplateMail): + def _render_message_body_as_html(self, context): + html = super()._render_message_body_as_html(context) + + # Transform CSS into line style attributes + return premailer.transform(html) + + +class MagicMailBuilder(template_mail.MagicMailBuilder): + pass + + +mail_builder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) diff --git a/taiga/base/management/commands/test_emails.py b/taiga/base/management/commands/test_emails.py index da535c7f..e0cd5e94 100644 --- a/taiga/base/management/commands/test_emails.py +++ b/taiga/base/management/commands/test_emails.py @@ -22,7 +22,7 @@ from django.db.models.loading import get_model from django.core.management.base import BaseCommand from django.utils import timezone -from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail +from taiga.base.mails import mail_builder from taiga.projects.models import Project, Membership from taiga.projects.history.models import HistoryEntry @@ -47,11 +47,12 @@ class Command(BaseCommand): locale = options.get('locale') test_email = args[0] - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - # Register email - context = {"lang": locale, "user": User.objects.all().order_by("?").first(), "cancel_token": "cancel-token"} - email = mbuilder.registered_user(test_email, context) + context = {"lang": locale, + "user": User.objects.all().order_by("?").first(), + "cancel_token": "cancel-token"} + + email = mail_builder.registered_user(test_email, context) email.send() # Membership invitation @@ -60,12 +61,13 @@ class Command(BaseCommand): membership.invitation_extra_text = "Text example, Text example,\nText example,\n\nText example" context = {"lang": locale, "membership": membership} - email = mbuilder.membership_invitation(test_email, context) + email = mail_builder.membership_invitation(test_email, context) email.send() # Membership notification - context = {"lang": locale, "membership": Membership.objects.order_by("?").filter(user__isnull=False).first()} - email = mbuilder.membership_notification(test_email, context) + context = {"lang": locale, + "membership": Membership.objects.order_by("?").filter(user__isnull=False).first()} + email = mail_builder.membership_notification(test_email, context) email.send() # Feedback @@ -81,17 +83,17 @@ class Command(BaseCommand): "key2": "value2", }, } - email = mbuilder.feedback_notification(test_email, context) + email = mail_builder.feedback_notification(test_email, context) email.send() # Password recovery context = {"lang": locale, "user": User.objects.all().order_by("?").first()} - email = mbuilder.password_recovery(test_email, context) + email = mail_builder.password_recovery(test_email, context) email.send() # Change email context = {"lang": locale, "user": User.objects.all().order_by("?").first()} - email = mbuilder.change_email(test_email, context) + email = mail_builder.change_email(test_email, context) email.send() # Export/Import emails @@ -102,7 +104,7 @@ class Command(BaseCommand): "error_subject": "Error generating project dump", "error_message": "Error generating project dump", } - email = mbuilder.export_error(test_email, context) + email = mail_builder.export_error(test_email, context) email.send() context = { "lang": locale, @@ -110,7 +112,7 @@ class Command(BaseCommand): "error_subject": "Error importing project dump", "error_message": "Error importing project dump", } - email = mbuilder.import_error(test_email, context) + email = mail_builder.import_error(test_email, context) email.send() deletion_date = timezone.now() + datetime.timedelta(seconds=60*60*24) @@ -121,7 +123,7 @@ class Command(BaseCommand): "project": Project.objects.all().order_by("?").first(), "deletion_date": deletion_date, } - email = mbuilder.dump_project(test_email, context) + email = mail_builder.dump_project(test_email, context) email.send() context = { @@ -129,7 +131,7 @@ class Command(BaseCommand): "user": User.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(), } - email = mbuilder.load_dump(test_email, context) + email = mail_builder.load_dump(test_email, context) email.send() # Notification emails diff --git a/taiga/base/neighbors.py b/taiga/base/neighbors.py index 4e77c9cc..545c130c 100644 --- a/taiga/base/neighbors.py +++ b/taiga/base/neighbors.py @@ -43,7 +43,7 @@ def get_neighbors(obj, results_set=None): query = """ SELECT * FROM - (SELECT "id" as id, ROW_NUMBER() OVER() + (SELECT "col1" as id, ROW_NUMBER() OVER() FROM (%s) as ID_AND_ROW) AS SELECTED_ID_AND_ROW """ % (base_sql) diff --git a/taiga/base/utils/contenttypes.py b/taiga/base/utils/contenttypes.py new file mode 100644 index 00000000..1a8aabcf --- /dev/null +++ b/taiga/base/utils/contenttypes.py @@ -0,0 +1,23 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 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 django.apps import apps +from django.contrib.contenttypes.management import update_contenttypes + + +def update_all_contenttypes(**kwargs): + for app_config in apps.get_app_configs(): + update_contenttypes(app_config, **kwargs) diff --git a/taiga/events/signal_handlers.py b/taiga/events/signal_handlers.py index 7f938f15..33b00e89 100644 --- a/taiga/events/signal_handlers.py +++ b/taiga/events/signal_handlers.py @@ -27,6 +27,8 @@ from . import events def on_save_any_model(sender, instance, created, **kwargs): # Ignore any object that can not have project_id + if not hasattr(instance, "project_id"): + return content_type = get_typename_for_model_instance(instance) # Ignore any other events diff --git a/taiga/export_import/tasks.py b/taiga/export_import/tasks.py index f833aef4..70a45f91 100644 --- a/taiga/export_import/tasks.py +++ b/taiga/export_import/tasks.py @@ -25,8 +25,7 @@ from django.utils import timezone from django.conf import settings from django.utils.translation import ugettext as _ -from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail - +from taiga.base.mails import mail_builder from taiga.celery import app from .service import render_project @@ -40,7 +39,6 @@ import resource @app.task(bind=True) def dump_project(self, user, project): - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) path = "exports/{}/{}-{}.json".format(project.pk, project.slug, self.request.id) storage_path = default_storage.path(path) @@ -56,7 +54,7 @@ def dump_project(self, user, project): "error_message": _("Error generating project dump"), "project": project } - email = mbuilder.export_error(user, ctx) + email = mail_builder.export_error(user, ctx) email.send() logger.error('Error generating dump %s (by %s)', project.slug, user, exc_info=sys.exc_info()) return @@ -68,7 +66,7 @@ def dump_project(self, user, project): "user": user, "deletion_date": deletion_date } - email = mbuilder.dump_project(user, ctx) + email = mail_builder.dump_project(user, ctx) email.send() @@ -79,8 +77,6 @@ def delete_project_dump(project_id, project_slug, task_id): @app.task def load_project_dump(user, dump): - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - try: project = dict_to_project(dump, user.email) except Exception: @@ -89,11 +85,11 @@ def load_project_dump(user, dump): "error_subject": _("Error loading project dump"), "error_message": _("Error loading project dump"), } - email = mbuilder.import_error(user, ctx) + email = mail_builder.import_error(user, ctx) email.send() logger.error('Error loading dump %s (by %s)', project.slug, user, exc_info=sys.exc_info()) return ctx = {"user": user, "project": project} - email = mbuilder.load_dump(user, ctx) + email = mail_builder.load_dump(user, ctx) email.send() diff --git a/taiga/feedback/services.py b/taiga/feedback/services.py index e5f92c3c..a0f16762 100644 --- a/taiga/feedback/services.py +++ b/taiga/feedback/services.py @@ -16,7 +16,7 @@ from django.conf import settings -from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail +from taiga.base.mails import mail_builder def send_feedback(feedback_entry, extra, reply_to=[]): @@ -30,7 +30,6 @@ def send_feedback(feedback_entry, extra, reply_to=[]): "extra": extra } - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - email = mbuilder.feedback_notification(support_email, ctx) + email = mail_builder.feedback_notification(support_email, ctx) email.extra_headers["Reply-To"] = ", ".join(reply_to) email.send() diff --git a/taiga/front/templatetags/functions.py b/taiga/front/templatetags/functions.py index 9a510d50..3391fece 100644 --- a/taiga/front/templatetags/functions.py +++ b/taiga/front/templatetags/functions.py @@ -21,10 +21,7 @@ from django_sites import get_by_id as get_site_by_id from taiga.front.urls import urls -register = library.Library() - - -@register.global_function(name="resolve_front_url") +@library.global_function(name="resolve_front_url") def resolve(type, *args): site = get_site_by_id("front") url_tmpl = "{scheme}//{domain}{url}" diff --git a/taiga/mdrender/service.py b/taiga/mdrender/service.py index 077c0c63..a507b1c2 100644 --- a/taiga/mdrender/service.py +++ b/taiga/mdrender/service.py @@ -75,11 +75,11 @@ def _make_extensions_list(project=None): MentionsExtension(), TaigaReferencesExtension(project), TargetBlankLinkExtension(), - "extra", - "codehilite", - "sane_lists", - "toc", - "nl2br"] + "markdown.extensions.extra", + "markdown.extensions.codehilite", + "markdown.extensions.sane_lists", + "markdown.extensions.toc", + "markdown.extensions.nl2br"] import diff_match_patch diff --git a/taiga/mdrender/templatetags/functions.py b/taiga/mdrender/templatetags/functions.py index 7608f553..88c7af86 100644 --- a/taiga/mdrender/templatetags/functions.py +++ b/taiga/mdrender/templatetags/functions.py @@ -18,10 +18,8 @@ from django_jinja import library from jinja2 import Markup from taiga.mdrender.service import render -register = library.Library() - -@register.global_function +@library.global_function def mdrender(project, text) -> str: if text: return Markup(render(project, text)) diff --git a/taiga/projects/history/templatetags/functions.py b/taiga/projects/history/templatetags/functions.py index 8d616b73..eadda2c6 100644 --- a/taiga/projects/history/templatetags/functions.py +++ b/taiga/projects/history/templatetags/functions.py @@ -18,8 +18,6 @@ from django.utils.translation import ugettext_lazy as _ from django_jinja import library -register = library.Library() - EXTRA_FIELD_VERBOSE_NAMES = { "description_diff": _("description"), @@ -29,7 +27,7 @@ EXTRA_FIELD_VERBOSE_NAMES = { } -@register.global_function +@library.global_function def verbose_name(obj_class, field_name): if field_name in EXTRA_FIELD_VERBOSE_NAMES: return EXTRA_FIELD_VERBOSE_NAMES[field_name] @@ -39,6 +37,7 @@ def verbose_name(obj_class, field_name): except Exception: return field_name -@register.global_function + +@library.global_function def lists_diff(list1, list2): return (list(set(list1) - set(list2))) diff --git a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py index 915ce22e..c612acb1 100644 --- a/taiga/projects/issues/migrations/0006_remove_issue_watchers.py +++ b/taiga/projects/issues/migrations/0006_remove_issue_watchers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes +from taiga.base.utils.contenttypes import update_all_contenttypes def create_notifications(apps, schema_editor): update_all_contenttypes(verbosity=0) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 26950c60..566bfe16 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -21,7 +21,6 @@ from django.core.management.base import BaseCommand from django.db import transaction from django.utils.timezone import now from django.conf import settings -from django.contrib.webdesign import lorem_ipsum from django.contrib.contenttypes.models import ContentType from sampledatahelper.helper import SampleDataHelper diff --git a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py index 162b6c1c..0d8a3a61 100644 --- a/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py +++ b/taiga/projects/milestones/migrations/0002_remove_milestone_watchers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes +from taiga.base.utils.contenttypes import update_all_contenttypes def create_notifications(apps, schema_editor): update_all_contenttypes(verbosity=0) diff --git a/taiga/projects/notifications/migrations/0005_auto_20151005_1357.py b/taiga/projects/notifications/migrations/0005_auto_20151005_1357.py new file mode 100644 index 00000000..3d38d5e6 --- /dev/null +++ b/taiga/projects/notifications/migrations/0005_auto_20151005_1357.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('notifications', '0004_watched'), + ] + + operations = [ + migrations.AlterField( + model_name='historychangenotification', + name='history_entries', + field=models.ManyToManyField(verbose_name='history entries', to='history.HistoryEntry', related_name='+'), + ), + migrations.AlterField( + model_name='historychangenotification', + name='notify_users', + field=models.ManyToManyField(verbose_name='notify users', to=settings.AUTH_USER_MODEL, related_name='+'), + ), + ] diff --git a/taiga/projects/notifications/models.py b/taiga/projects/notifications/models.py index 603bdd85..6f224588 100644 --- a/taiga/projects/notifications/models.py +++ b/taiga/projects/notifications/models.py @@ -61,10 +61,10 @@ class HistoryChangeNotification(models.Model): verbose_name=_("created date time")) updated_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True, verbose_name=_("updated date time")) - history_entries = models.ManyToManyField("history.HistoryEntry", null=True, blank=True, + history_entries = models.ManyToManyField("history.HistoryEntry", verbose_name=_("history entries"), related_name="+") - notify_users = models.ManyToManyField("users.User", null=True, blank=True, + notify_users = models.ManyToManyField("users.User", verbose_name=_("notify users"), related_name="+") project = models.ForeignKey("projects.Project", null=False, blank=False, diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index e4bd8a3f..6b805170 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -28,9 +28,8 @@ from django.utils import timezone from django.conf import settings from django.utils.translation import ugettext as _ -from djmail import template_mail - from taiga.base import exceptions as exc +from taiga.base.mails import InlineCSSTemplateMail from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.history.choices import HistoryType from taiga.projects.history.services import (make_key_from_model_object, @@ -202,7 +201,7 @@ def _make_template_mail(name:str): of it. """ cls = type("InlineCSSTemplateMail", - (template_mail.InlineCSSTemplateMail,), + (InlineCSSTemplateMail,), {"name": name}) return cls() diff --git a/taiga/projects/services/invitations.py b/taiga/projects/services/invitations.py index 4196612c..50e1e01e 100644 --- a/taiga/projects/services/invitations.py +++ b/taiga/projects/services/invitations.py @@ -1,17 +1,16 @@ from django.apps import apps from django.conf import settings -from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail +from taiga.base.mails import mail_builder def send_invitation(invitation): """Send an invitation email""" - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) if invitation.user: - template = mbuilder.membership_notification + template = mail_builder.membership_notification email = template(invitation.user, {"membership": invitation}) else: - template = mbuilder.membership_invitation + template = mail_builder.membership_invitation email = template(invitation.email, {"membership": invitation}) email.send() diff --git a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py index 5a8b0618..639e12b1 100644 --- a/taiga/projects/tasks/migrations/0008_remove_task_watchers.py +++ b/taiga/projects/tasks/migrations/0008_remove_task_watchers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes +from taiga.base.utils.contenttypes import update_all_contenttypes def create_notifications(apps, schema_editor): update_all_contenttypes(verbosity=0) diff --git a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py index e2ff7538..e3b94599 100644 --- a/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py +++ b/taiga/projects/userstories/migrations/0010_remove_userstory_watchers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes +from taiga.base.utils.contenttypes import update_all_contenttypes def create_notifications(apps, schema_editor): update_all_contenttypes(verbosity=0) diff --git a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py index 7d7af0b6..44d6d5ac 100644 --- a/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py +++ b/taiga/projects/wiki/migrations/0002_remove_wikipage_watchers.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals from django.db import connection from django.db import models, migrations from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.management import update_all_contenttypes +from taiga.base.utils.contenttypes import update_all_contenttypes def create_notifications(apps, schema_editor): update_all_contenttypes(verbosity=0) diff --git a/taiga/users/api.py b/taiga/users/api.py index 1d41d5e3..b635b1cd 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -33,13 +33,11 @@ from taiga.base.api import ModelCrudViewSet from taiga.base.filters import PermissionBasedFilterBackend from taiga.base.api.utils import get_object_or_404 from taiga.base.filters import MembersFilterBackend +from taiga.base.mails import mail_builder from taiga.projects.votes import services as votes_service from easy_thumbnails.source_generators import pil_image -from djmail.template_mail import MagicMailBuilder -from djmail.template_mail import InlineCSSTemplateMail - from . import models from . import serializers from . import permissions @@ -189,8 +187,7 @@ class UsersViewSet(ModelCrudViewSet): user.token = str(uuid.uuid1()) user.save(update_fields=["token"]) - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - email = mbuilder.password_recovery(user, {"user": user}) + email = mail_builder.password_recovery(user, {"user": user}) email.send() return response.Ok({"detail": _("Mail sended successful!")}) @@ -314,8 +311,7 @@ class UsersViewSet(ModelCrudViewSet): request.user.email_token = str(uuid.uuid1()) request.user.new_email = new_email request.user.save(update_fields=["email_token", "new_email"]) - mbuilder = MagicMailBuilder(template_mail_cls=InlineCSSTemplateMail) - email = mbuilder.change_email(request.user.new_email, {"user": request.user, + email = mail_builder.change_email(request.user.new_email, {"user": request.user, "lang": request.user.lang}) email.send() diff --git a/taiga/users/forms.py b/taiga/users/forms.py index 1e7bb7ab..a5312c6a 100644 --- a/taiga/users/forms.py +++ b/taiga/users/forms.py @@ -41,4 +41,4 @@ class UserCreationForm(DjangoUserCreationForm): class UserChangeForm(DjangoUserChangeForm): class Meta: model = User - + fields = '__all__' diff --git a/taiga/users/migrations/0014_auto_20151005_1357.py b/taiga/users/migrations/0014_auto_20151005_1357.py new file mode 100644 index 00000000..ee24e169 --- /dev/null +++ b/taiga/users/migrations/0014_auto_20151005_1357.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.contrib.auth.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0013_auto_20150901_1600'), + ] + + operations = [ + migrations.AlterModelManagers( + name='user', + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + migrations.AlterField( + model_name='user', + name='last_login', + field=models.DateTimeField(verbose_name='last login', blank=True, null=True), + ), + migrations.AlterField( + model_name='user', + name='new_email', + field=models.EmailField(verbose_name='new email address', blank=True, null=True, max_length=254), + ), + ] diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index d183a9ef..7626769b 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -355,7 +355,7 @@ def test_watching_users_to_notify_on_issue_modification_6(): assert users == {watching_user, issue.owner} -def test_send_notifications_using_services_method(settings, mail): +def test_send_notifications_using_services_method_for_user_stories(settings, mail): settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 project = f.ProjectFactory.create() @@ -363,38 +363,34 @@ def test_send_notifications_using_services_method(settings, mail): member1 = f.MembershipFactory.create(project=project, role=role) member2 = f.MembershipFactory.create(project=project, role=role) - history_change = MagicMock() - history_change.user = {"pk": member1.user.pk} - history_change.comment = "" - history_change.type = HistoryType.change - history_change.is_hidden = False - - history_create = MagicMock() - history_create.user = {"pk": member1.user.pk} - history_create.comment = "" - history_create.type = HistoryType.create - history_create.is_hidden = False - - history_delete = MagicMock() - history_delete.user = {"pk": member1.user.pk} - history_delete.comment = "" - history_delete.type = HistoryType.delete - history_delete.is_hidden = False - - # Issues - issue = f.IssueFactory.create(project=project, owner=member2.user) - take_snapshot(issue, user=issue.owner) - services.send_notifications(issue, - history=history_create) - - services.send_notifications(issue, - history=history_change) - - services.send_notifications(issue, - history=history_delete) - - # Userstories us = f.UserStoryFactory.create(project=project, owner=member2.user) + history_change = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.change, + key="userstories.userstory:{}".format(us.id), + is_hidden=False, + diff=[] + ) + + history_create = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.create, + key="userstories.userstory:{}".format(us.id), + is_hidden=False, + diff=[] + ) + + history_delete = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="test:delete", + type=HistoryType.delete, + key="userstories.userstory:{}".format(us.id), + is_hidden=False, + diff=[] + ) + take_snapshot(us, user=us.owner) services.send_notifications(us, history=history_create) @@ -405,63 +401,25 @@ def test_send_notifications_using_services_method(settings, mail): services.send_notifications(us, history=history_delete) - # Tasks - task = f.TaskFactory.create(project=project, owner=member2.user) - take_snapshot(task, user=task.owner) - services.send_notifications(task, - history=history_create) - - services.send_notifications(task, - history=history_change) - - services.send_notifications(task, - history=history_delete) - - # Wiki pages - wiki = f.WikiPageFactory.create(project=project, owner=member2.user) - take_snapshot(wiki, user=wiki.owner) - services.send_notifications(wiki, - history=history_create) - - services.send_notifications(wiki, - history=history_change) - - services.send_notifications(wiki, - history=history_delete) - - assert models.HistoryChangeNotification.objects.count() == 12 + assert models.HistoryChangeNotification.objects.count() == 3 assert len(mail.outbox) == 0 time.sleep(1) services.process_sync_notifications() - assert len(mail.outbox) == 12 + assert len(mail.outbox) == 3 # test headers - events = [issue, us, task, wiki] domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] - i = 0 for msg in mail.outbox: - # each event has 3 msgs - event = events[math.floor(i / 3)] + m_id = "{project_slug}/{msg_id}".format( + project_slug=project.slug, + msg_id=us.ref + ) - # each set of 3 should have the same headers - if i % 3 == 0: - if hasattr(event, 'ref'): - e_slug = event.ref - elif hasattr(event, 'slug'): - e_slug = event.slug - else: - e_slug = 'taiga-system' - - m_id = "{project_slug}/{msg_id}".format( - project_slug=project.slug, - msg_id=e_slug - ) - - message_id = "<{m_id}/".format(m_id=m_id) - message_id_domain = "@{domain}>".format(domain=domain) - in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) - list_id = "Taiga/{p_name} " \ - .format(p_name=project.name, p_slug=project.slug, domain=domain) + message_id = "<{m_id}/".format(m_id=m_id) + message_id_domain = "@{domain}>".format(domain=domain) + in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) + list_id = "Taiga/{p_name} " \ + .format(p_name=project.name, p_slug=project.slug, domain=domain) assert msg.extra_headers headers = msg.extra_headers @@ -490,7 +448,286 @@ def test_send_notifications_using_services_method(settings, mail): msg_ts = datetime.datetime.fromtimestamp(int(msg_time)) assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index') - i += 1 + +def test_send_notifications_using_services_method_for_tasks(settings, mail): + settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 + + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=['view_issues', 'view_us', 'view_tasks', 'view_wiki_pages']) + member1 = f.MembershipFactory.create(project=project, role=role) + member2 = f.MembershipFactory.create(project=project, role=role) + + task = f.TaskFactory.create(project=project, owner=member2.user) + history_change = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.change, + key="tasks.task:{}".format(task.id), + is_hidden=False, + diff=[] + ) + + history_create = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.create, + key="tasks.task:{}".format(task.id), + is_hidden=False, + diff=[] + ) + + history_delete = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="test:delete", + type=HistoryType.delete, + key="tasks.task:{}".format(task.id), + is_hidden=False, + diff=[] + ) + + take_snapshot(task, user=task.owner) + services.send_notifications(task, + history=history_create) + + services.send_notifications(task, + history=history_change) + + services.send_notifications(task, + history=history_delete) + + assert models.HistoryChangeNotification.objects.count() == 3 + assert len(mail.outbox) == 0 + time.sleep(1) + services.process_sync_notifications() + assert len(mail.outbox) == 3 + + # test headers + domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] + for msg in mail.outbox: + m_id = "{project_slug}/{msg_id}".format( + project_slug=project.slug, + msg_id=task.ref + ) + + message_id = "<{m_id}/".format(m_id=m_id) + message_id_domain = "@{domain}>".format(domain=domain) + in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) + list_id = "Taiga/{p_name} " \ + .format(p_name=project.name, p_slug=project.slug, domain=domain) + + assert msg.extra_headers + headers = msg.extra_headers + + # can't test the time part because it's set when sending + # check what we can + assert 'Message-ID' in headers + assert message_id in headers.get('Message-ID') + assert message_id_domain in headers.get('Message-ID') + + assert 'In-Reply-To' in headers + assert in_reply_to == headers.get('In-Reply-To') + assert 'References' in headers + assert in_reply_to == headers.get('References') + + assert 'List-ID' in headers + assert list_id == headers.get('List-ID') + + assert 'Thread-Index' in headers + # always is b64 encoded 22 bytes + assert len(base64.b64decode(headers.get('Thread-Index'))) == 22 + + # hashes should match for identical ids and times + # we check the actual method in test_ms_thread_id() + msg_time = headers.get('Message-ID').split('/')[2].split('@')[0] + msg_ts = datetime.datetime.fromtimestamp(int(msg_time)) + assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index') + + +def test_send_notifications_using_services_method_for_issues(settings, mail): + settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 + + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=['view_issues', 'view_us', 'view_tasks', 'view_wiki_pages']) + member1 = f.MembershipFactory.create(project=project, role=role) + member2 = f.MembershipFactory.create(project=project, role=role) + + issue = f.IssueFactory.create(project=project, owner=member2.user) + history_change = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.change, + key="issues.issue:{}".format(issue.id), + is_hidden=False, + diff=[] + ) + + history_create = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.create, + key="issues.issue:{}".format(issue.id), + is_hidden=False, + diff=[] + ) + + history_delete = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="test:delete", + type=HistoryType.delete, + key="issues.issue:{}".format(issue.id), + is_hidden=False, + diff=[] + ) + + take_snapshot(issue, user=issue.owner) + services.send_notifications(issue, + history=history_create) + + services.send_notifications(issue, + history=history_change) + + services.send_notifications(issue, + history=history_delete) + + assert models.HistoryChangeNotification.objects.count() == 3 + assert len(mail.outbox) == 0 + time.sleep(1) + services.process_sync_notifications() + assert len(mail.outbox) == 3 + + # test headers + domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] + for msg in mail.outbox: + m_id = "{project_slug}/{msg_id}".format( + project_slug=project.slug, + msg_id=issue.ref + ) + + message_id = "<{m_id}/".format(m_id=m_id) + message_id_domain = "@{domain}>".format(domain=domain) + in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) + list_id = "Taiga/{p_name} " \ + .format(p_name=project.name, p_slug=project.slug, domain=domain) + + assert msg.extra_headers + headers = msg.extra_headers + + # can't test the time part because it's set when sending + # check what we can + assert 'Message-ID' in headers + assert message_id in headers.get('Message-ID') + assert message_id_domain in headers.get('Message-ID') + + assert 'In-Reply-To' in headers + assert in_reply_to == headers.get('In-Reply-To') + assert 'References' in headers + assert in_reply_to == headers.get('References') + + assert 'List-ID' in headers + assert list_id == headers.get('List-ID') + + assert 'Thread-Index' in headers + # always is b64 encoded 22 bytes + assert len(base64.b64decode(headers.get('Thread-Index'))) == 22 + + # hashes should match for identical ids and times + # we check the actual method in test_ms_thread_id() + msg_time = headers.get('Message-ID').split('/')[2].split('@')[0] + msg_ts = datetime.datetime.fromtimestamp(int(msg_time)) + assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index') + + +def test_send_notifications_using_services_method_for_wiki_pages(settings, mail): + settings.CHANGE_NOTIFICATIONS_MIN_INTERVAL = 1 + + project = f.ProjectFactory.create() + role = f.RoleFactory.create(project=project, permissions=['view_issues', 'view_us', 'view_tasks', 'view_wiki_pages']) + member1 = f.MembershipFactory.create(project=project, role=role) + member2 = f.MembershipFactory.create(project=project, role=role) + + wiki = f.WikiPageFactory.create(project=project, owner=member2.user) + history_change = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.change, + key="wiki.wikipage:{}".format(wiki.id), + is_hidden=False, + diff=[] + ) + + history_create = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="", + type=HistoryType.create, + key="wiki.wikipage:{}".format(wiki.id), + is_hidden=False, + diff=[] + ) + + history_delete = f.HistoryEntryFactory.create( + user={"pk": member1.user.id}, + comment="test:delete", + type=HistoryType.delete, + key="wiki.wikipage:{}".format(wiki.id), + is_hidden=False, + diff=[] + ) + take_snapshot(wiki, user=wiki.owner) + services.send_notifications(wiki, + history=history_create) + + services.send_notifications(wiki, + history=history_change) + + services.send_notifications(wiki, + history=history_delete) + + assert models.HistoryChangeNotification.objects.count() == 3 + assert len(mail.outbox) == 0 + time.sleep(1) + services.process_sync_notifications() + assert len(mail.outbox) == 3 + + # test headers + domain = settings.SITES["api"]["domain"].split(":")[0] or settings.SITES["api"]["domain"] + for msg in mail.outbox: + m_id = "{project_slug}/{msg_id}".format( + project_slug=project.slug, + msg_id=wiki.slug + ) + + message_id = "<{m_id}/".format(m_id=m_id) + message_id_domain = "@{domain}>".format(domain=domain) + in_reply_to = "<{m_id}@{domain}>".format(m_id=m_id, domain=domain) + list_id = "Taiga/{p_name} " \ + .format(p_name=project.name, p_slug=project.slug, domain=domain) + + assert msg.extra_headers + headers = msg.extra_headers + + # can't test the time part because it's set when sending + # check what we can + assert 'Message-ID' in headers + assert message_id in headers.get('Message-ID') + assert message_id_domain in headers.get('Message-ID') + + assert 'In-Reply-To' in headers + assert in_reply_to == headers.get('In-Reply-To') + assert 'References' in headers + assert in_reply_to == headers.get('References') + + assert 'List-ID' in headers + assert list_id == headers.get('List-ID') + + assert 'Thread-Index' in headers + # always is b64 encoded 22 bytes + assert len(base64.b64decode(headers.get('Thread-Index'))) == 22 + + # hashes should match for identical ids and times + # we check the actual method in test_ms_thread_id() + msg_time = headers.get('Message-ID').split('/')[2].split('@')[0] + msg_ts = datetime.datetime.fromtimestamp(int(msg_time)) + assert services.make_ms_thread_index(in_reply_to, msg_ts) == headers.get('Thread-Index') def test_resource_notification_test(client, settings, mail): diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index 582c3667..b526ac76 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -200,7 +200,6 @@ def test_update_project_timeline(): project = factories.ProjectFactory.create(name="test project timeline") history_services.take_snapshot(project, user=project.owner) project.add_watcher(user_watcher) - print("PPPP") project.name = "test project timeline updated" project.save() history_services.take_snapshot(project, user=project.owner) diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 302a51a3..a9d4c1cd 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -483,9 +483,6 @@ def test_get_voted_list_valid_info_for_project(): assert project_vote_info["is_private"] == project.is_private - import pprint - pprint.pprint(project_vote_info) - assert project_vote_info["is_fan"] == False assert project_vote_info["is_watcher"] == False assert project_vote_info["total_watchers"] == 0 diff --git a/tests/integration/test_userstories.py b/tests/integration/test_userstories.py index 6506f70f..4ddaece0 100644 --- a/tests/integration/test_userstories.py +++ b/tests/integration/test_userstories.py @@ -57,7 +57,7 @@ def test_create_userstory_with_watchers(client): data = {"subject": "Test user story", "project": project.id, "watchers": [user_watcher.id]} client.login(user) response = client.json.post(url, json.dumps(data)) - print(response.data) + assert response.status_code == 201 assert response.data["watchers"] == [] diff --git a/tests/unit/test_export.py b/tests/unit/test_export.py index 0a6ba9d6..f95e44bd 100644 --- a/tests/unit/test_export.py +++ b/tests/unit/test_export.py @@ -28,7 +28,6 @@ def test_export_issue_finish_date(client): issue = f.IssueFactory.create(finished_date="2014-10-22") output = io.StringIO() render_project(issue.project, output) - print(output.getvalue()) project_data = json.loads(output.getvalue()) finish_date = project_data["issues"][0]["finished_date"] assert finish_date == "2014-10-22T00:00:00+0000" diff --git a/tests/unit/test_mdrender.py b/tests/unit/test_mdrender.py index 1de084d8..d0404c13 100644 --- a/tests/unit/test_mdrender.py +++ b/tests/unit/test_mdrender.py @@ -165,8 +165,8 @@ def test_render_relative_image(): def test_render_triple_quote_code(): - expected_result = "
print(\"test\")\n
" - assert render(dummy_project, "```\nprint(\"test\")\n```") == expected_result + expected_result = "
print(\"test\")\n
" + assert render(dummy_project, "```python\nprint(\"test\")\n```") == expected_result def test_render_triple_quote_and_lang_code(): From 9b56a414f05e463563853420071fdd5d0fb3b88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 12 Oct 2015 10:29:52 +0200 Subject: [PATCH 161/190] Fix dates in license messages. Change 2014 to 2014-2015 --- doc/source/conf.py | 6 +++--- settings/__init__.py | 6 +++--- settings/celery.py | 6 +++--- settings/common.py | 6 +++--- settings/development.py | 6 +++--- settings/local.py.example | 6 +++--- settings/sr.py | 6 +++--- settings/testing.py | 6 +++--- settings/travis.py | 6 +++--- taiga/__init__.py | 6 +++--- taiga/auth/api.py | 6 +++--- taiga/auth/backends.py | 6 +++--- taiga/auth/permissions.py | 4 ++-- taiga/auth/serializers.py | 6 +++--- taiga/auth/services.py | 6 +++--- taiga/auth/signals.py | 6 +++--- taiga/auth/tokens.py | 6 +++--- taiga/base/__init__.py | 6 +++--- taiga/base/api/__init__.py | 6 +++--- taiga/base/api/authentication.py | 6 +++--- taiga/base/api/fields.py | 6 +++--- taiga/base/api/generics.py | 6 +++--- taiga/base/api/mixins.py | 6 +++--- taiga/base/api/negotiation.py | 6 +++--- taiga/base/api/pagination.py | 6 +++--- taiga/base/api/parsers.py | 6 +++--- taiga/base/api/permissions.py | 6 +++--- taiga/base/api/relations.py | 6 +++--- taiga/base/api/renderers.py | 6 +++--- taiga/base/api/request.py | 6 +++--- taiga/base/api/reverse.py | 6 +++--- taiga/base/api/serializers.py | 6 +++--- taiga/base/api/settings.py | 6 +++--- taiga/base/api/static/api/css/bootstrap-tweaks.css | 6 +++--- taiga/base/api/static/api/css/default.css | 6 +++--- taiga/base/api/static/api/css/prettify.css | 6 +++--- taiga/base/api/static/api/js/default.js | 6 +++--- taiga/base/api/static/api/js/prettify-min.js | 6 +++--- taiga/base/api/templatetags/api.py | 6 +++--- taiga/base/api/throttling.py | 6 +++--- taiga/base/api/urlpatterns.py | 6 +++--- taiga/base/api/urls.py | 6 +++--- taiga/base/api/utils.py | 6 +++--- taiga/base/api/utils/__init__.py | 6 +++--- taiga/base/api/utils/breadcrumbs.py | 6 +++--- taiga/base/api/utils/encoders.py | 6 +++--- taiga/base/api/utils/formatting.py | 6 +++--- taiga/base/api/utils/mediatypes.py | 6 +++--- taiga/base/api/views.py | 6 +++--- taiga/base/api/viewsets.py | 6 +++--- taiga/base/apps.py | 6 +++--- taiga/base/connectors/exceptions.py | 6 +++--- taiga/base/decorators.py | 6 +++--- taiga/base/exceptions.py | 6 +++--- taiga/base/fields.py | 6 +++--- taiga/base/filters.py | 6 +++--- taiga/base/formats/en/formats.py | 6 +++--- taiga/base/formats/es/formats.py | 6 +++--- taiga/base/mails.py | 6 +++--- taiga/base/management/commands/test_emails.py | 6 +++--- taiga/base/middleware/cors.py | 6 +++--- taiga/base/neighbors.py | 8 ++++---- taiga/base/response.py | 8 ++++---- taiga/base/routers.py | 6 +++--- taiga/base/status.py | 6 +++--- taiga/base/storage.py | 6 +++--- taiga/base/tags.py | 8 ++++---- taiga/base/throttling.py | 6 +++--- taiga/base/utils/contenttypes.py | 6 +++--- taiga/base/utils/db.py | 6 +++--- taiga/base/utils/dicts.py | 6 +++--- taiga/base/utils/diff.py | 6 +++--- taiga/base/utils/functions.py | 8 ++++---- taiga/base/utils/iterators.py | 8 ++++---- taiga/base/utils/json.py | 6 +++--- taiga/base/utils/sequence.py | 6 +++--- taiga/base/utils/signals.py | 8 ++++---- taiga/base/utils/slug.py | 6 +++--- taiga/base/utils/text.py | 6 +++--- taiga/base/utils/urls.py | 8 ++++---- taiga/celery.py | 6 +++--- taiga/contrib_routers.py | 6 +++--- taiga/deferred.py | 6 +++--- taiga/events/__init__.py | 6 +++--- taiga/events/apps.py | 6 +++--- taiga/events/backends/__init__.py | 6 +++--- taiga/events/backends/base.py | 2 +- taiga/events/backends/postgresql.py | 2 +- taiga/events/backends/rabbitmq.py | 2 +- taiga/events/events.py | 2 +- taiga/events/middleware.py | 2 +- taiga/events/signal_handlers.py | 6 +++--- taiga/export_import/api.py | 6 +++--- taiga/export_import/dump_service.py | 6 +++--- taiga/export_import/management/commands/dump_project.py | 6 +++--- taiga/export_import/management/commands/load_dump.py | 6 +++--- taiga/export_import/mixins.py | 6 +++--- taiga/export_import/permissions.py | 6 +++--- taiga/export_import/renderers.py | 6 +++--- taiga/export_import/serializers.py | 6 +++--- taiga/export_import/service.py | 6 +++--- taiga/export_import/tasks.py | 6 +++--- taiga/export_import/throttling.py | 6 +++--- taiga/external_apps/admin.py | 6 +++--- taiga/external_apps/api.py | 6 +++--- taiga/external_apps/auth_backends.py | 6 +++--- taiga/external_apps/encryption.py | 6 +++--- taiga/external_apps/models.py | 6 +++--- taiga/external_apps/permissions.py | 6 +++--- taiga/external_apps/serializers.py | 6 +++--- taiga/external_apps/services.py | 6 +++--- taiga/feedback/__init__.py | 6 +++--- taiga/feedback/admin.py | 6 +++--- taiga/feedback/api.py | 6 +++--- taiga/feedback/apps.py | 6 +++--- taiga/feedback/models.py | 6 +++--- taiga/feedback/permissions.py | 6 +++--- taiga/feedback/routers.py | 6 +++--- taiga/feedback/serializers.py | 6 +++--- taiga/feedback/services.py | 6 +++--- taiga/front/sitemaps/__init__.py | 4 ++-- taiga/front/sitemaps/base.py | 4 ++-- taiga/front/sitemaps/generics.py | 4 ++-- taiga/front/sitemaps/issues.py | 4 ++-- taiga/front/sitemaps/milestones.py | 4 ++-- taiga/front/sitemaps/projects.py | 4 ++-- taiga/front/sitemaps/tasks.py | 4 ++-- taiga/front/sitemaps/users.py | 4 ++-- taiga/front/sitemaps/userstories.py | 4 ++-- taiga/front/sitemaps/wiki.py | 4 ++-- taiga/front/templatetags/functions.py | 6 +++--- taiga/front/urls.py | 6 +++--- taiga/hooks/api.py | 6 +++--- taiga/hooks/bitbucket/api.py | 6 +++--- taiga/hooks/bitbucket/event_hooks.py | 6 +++--- taiga/hooks/bitbucket/services.py | 6 +++--- taiga/hooks/event_hooks.py | 6 +++--- taiga/hooks/exceptions.py | 6 +++--- taiga/hooks/github/api.py | 6 +++--- taiga/hooks/github/event_hooks.py | 6 +++--- taiga/hooks/github/services.py | 6 +++--- taiga/hooks/gitlab/api.py | 6 +++--- taiga/hooks/gitlab/event_hooks.py | 6 +++--- taiga/hooks/gitlab/services.py | 6 +++--- taiga/locale/api.py | 6 +++--- taiga/locale/ca/LC_MESSAGES/django.po | 2 +- taiga/locale/de/LC_MESSAGES/django.po | 2 +- taiga/locale/en/LC_MESSAGES/django.po | 2 +- taiga/locale/es/LC_MESSAGES/django.po | 2 +- taiga/locale/fi/LC_MESSAGES/django.po | 2 +- taiga/locale/fr/LC_MESSAGES/django.po | 2 +- taiga/locale/it/LC_MESSAGES/django.po | 2 +- taiga/locale/nl/LC_MESSAGES/django.po | 2 +- taiga/locale/permissions.py | 6 +++--- taiga/locale/pl/LC_MESSAGES/django.po | 2 +- taiga/locale/pt_BR/LC_MESSAGES/django.po | 2 +- taiga/locale/ru/LC_MESSAGES/django.po | 2 +- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 2 +- taiga/mdrender/__init__.py | 6 +++--- taiga/mdrender/extensions/target_link.py | 8 ++++---- taiga/mdrender/extensions/wikilinks.py | 8 ++++---- taiga/mdrender/service.py | 6 +++--- taiga/mdrender/templatetags/functions.py | 6 +++--- taiga/permissions/permissions.py | 8 ++++---- taiga/permissions/service.py | 8 ++++---- taiga/projects/__init__.py | 6 +++--- taiga/projects/admin.py | 6 +++--- taiga/projects/api.py | 6 +++--- taiga/projects/apps.py | 6 +++--- taiga/projects/attachments/__init__.py | 6 +++--- taiga/projects/attachments/admin.py | 6 +++--- taiga/projects/attachments/api.py | 6 +++--- taiga/projects/attachments/apps.py | 6 +++--- taiga/projects/attachments/models.py | 6 +++--- taiga/projects/attachments/permissions.py | 6 +++--- taiga/projects/attachments/serializers.py | 6 +++--- taiga/projects/choices.py | 6 +++--- taiga/projects/custom_attributes/admin.py | 6 +++--- taiga/projects/custom_attributes/api.py | 6 +++--- taiga/projects/custom_attributes/models.py | 6 +++--- taiga/projects/custom_attributes/permissions.py | 6 +++--- taiga/projects/custom_attributes/serializers.py | 6 +++--- taiga/projects/custom_attributes/services.py | 6 +++--- taiga/projects/custom_attributes/signals.py | 6 +++--- taiga/projects/history/api.py | 6 +++--- taiga/projects/history/choices.py | 2 +- taiga/projects/history/freeze_impl.py | 6 +++--- taiga/projects/history/mixins.py | 6 +++--- taiga/projects/history/models.py | 2 +- taiga/projects/history/permissions.py | 6 +++--- taiga/projects/history/serializers.py | 6 +++--- taiga/projects/history/services.py | 2 +- taiga/projects/history/templatetags/functions.py | 6 +++--- taiga/projects/issues/__init__.py | 6 +++--- taiga/projects/issues/admin.py | 6 +++--- taiga/projects/issues/api.py | 6 +++--- taiga/projects/issues/apps.py | 6 +++--- taiga/projects/issues/models.py | 6 +++--- taiga/projects/issues/permissions.py | 6 +++--- taiga/projects/issues/serializers.py | 6 +++--- taiga/projects/issues/services.py | 6 +++--- taiga/projects/issues/signals.py | 6 +++--- taiga/projects/management/commands/sample_data.py | 6 +++--- taiga/projects/milestones/admin.py | 6 +++--- taiga/projects/milestones/api.py | 6 +++--- taiga/projects/milestones/models.py | 6 +++--- taiga/projects/milestones/permissions.py | 6 +++--- taiga/projects/milestones/serializers.py | 6 +++--- taiga/projects/milestones/services.py | 6 +++--- taiga/projects/mixins/blocked.py | 6 +++--- taiga/projects/mixins/on_destroy.py | 6 +++--- taiga/projects/mixins/ordering.py | 6 +++--- taiga/projects/models.py | 6 +++--- taiga/projects/notifications/admin.py | 6 +++--- taiga/projects/notifications/api.py | 6 +++--- taiga/projects/notifications/choices.py | 6 +++--- .../management/commands/send_notifications.py | 6 +++--- taiga/projects/notifications/mixins.py | 6 +++--- taiga/projects/notifications/models.py | 6 +++--- taiga/projects/notifications/permissions.py | 6 +++--- taiga/projects/notifications/serializers.py | 6 +++--- taiga/projects/notifications/services.py | 6 +++--- taiga/projects/notifications/utils.py | 8 ++++---- taiga/projects/notifications/validators.py | 6 +++--- taiga/projects/occ/__init__.py | 6 +++--- taiga/projects/occ/mixins.py | 6 +++--- taiga/projects/permissions.py | 6 +++--- taiga/projects/references/api.py | 6 +++--- taiga/projects/references/models.py | 6 +++--- taiga/projects/references/permissions.py | 6 +++--- taiga/projects/references/sequences.py | 6 +++--- taiga/projects/references/serializers.py | 6 +++--- taiga/projects/references/services.py | 6 +++--- taiga/projects/serializers.py | 6 +++--- taiga/projects/services/__init__.py | 6 +++--- taiga/projects/services/bulk_update_order.py | 6 +++--- taiga/projects/services/filters.py | 6 +++--- taiga/projects/services/modules_config.py | 6 +++--- taiga/projects/services/stats.py | 6 +++--- taiga/projects/services/tags_colors.py | 6 +++--- taiga/projects/signals.py | 6 +++--- taiga/projects/tasks/__init__.py | 6 +++--- taiga/projects/tasks/admin.py | 6 +++--- taiga/projects/tasks/api.py | 6 +++--- taiga/projects/tasks/apps.py | 6 +++--- taiga/projects/tasks/models.py | 6 +++--- taiga/projects/tasks/permissions.py | 6 +++--- taiga/projects/tasks/serializers.py | 6 +++--- taiga/projects/tasks/services.py | 6 +++--- taiga/projects/tasks/signals.py | 6 +++--- taiga/projects/translations.py | 6 +++--- taiga/projects/userstories/__init__.py | 6 +++--- taiga/projects/userstories/admin.py | 6 +++--- taiga/projects/userstories/api.py | 6 +++--- taiga/projects/userstories/apps.py | 6 +++--- taiga/projects/userstories/models.py | 6 +++--- taiga/projects/userstories/permissions.py | 6 +++--- taiga/projects/userstories/serializers.py | 6 +++--- taiga/projects/userstories/services.py | 6 +++--- taiga/projects/userstories/signals.py | 6 +++--- taiga/projects/userstories/validators.py | 6 +++--- taiga/projects/validators.py | 6 +++--- taiga/projects/votes/admin.py | 6 +++--- taiga/projects/votes/mixins/serializers.py | 6 +++--- taiga/projects/votes/mixins/viewsets.py | 6 +++--- taiga/projects/votes/models.py | 8 ++++---- taiga/projects/votes/serializers.py | 8 ++++---- taiga/projects/votes/services.py | 8 ++++---- taiga/projects/votes/utils.py | 8 ++++---- taiga/projects/wiki/admin.py | 6 +++--- taiga/projects/wiki/api.py | 6 +++--- taiga/projects/wiki/models.py | 6 +++--- taiga/projects/wiki/permissions.py | 6 +++--- taiga/projects/wiki/serializers.py | 6 +++--- taiga/routers.py | 6 +++--- taiga/searches/api.py | 6 +++--- taiga/searches/serializers.py | 6 +++--- taiga/searches/services.py | 6 +++--- taiga/stats/__init__.py | 2 +- taiga/stats/api.py | 2 +- taiga/stats/apps.py | 2 +- taiga/stats/permissions.py | 2 +- taiga/stats/routers.py | 2 +- taiga/stats/services.py | 2 +- taiga/timeline/__init__.py | 6 +++--- taiga/timeline/api.py | 6 +++--- taiga/timeline/apps.py | 6 +++--- .../commands/_clear_unnecessary_new_membership_entries.py | 6 +++--- .../commands/_rebuild_timeline_for_user_creation.py | 6 +++--- .../commands/_update_timeline_for_updated_tasks.py | 6 +++--- taiga/timeline/management/commands/rebuild_timeline.py | 6 +++--- .../commands/rebuild_timeline_iterating_per_projects.py | 6 +++--- taiga/timeline/models.py | 6 +++--- taiga/timeline/permissions.py | 6 +++--- taiga/timeline/serializers.py | 6 +++--- taiga/timeline/service.py | 6 +++--- taiga/timeline/signals.py | 6 +++--- taiga/timeline/timeline_implementations.py | 6 +++--- taiga/urls.py | 6 +++--- taiga/users/admin.py | 6 +++--- taiga/users/api.py | 6 +++--- taiga/users/filters.py | 6 +++--- taiga/users/forms.py | 6 +++--- taiga/users/gravatar.py | 8 ++++---- taiga/users/models.py | 6 +++--- taiga/users/permissions.py | 6 +++--- taiga/users/serializers.py | 6 +++--- taiga/users/services.py | 6 +++--- taiga/users/signals.py | 6 +++--- taiga/users/validators.py | 8 ++++---- taiga/userstorage/api.py | 6 +++--- taiga/userstorage/filters.py | 6 +++--- taiga/userstorage/models.py | 6 +++--- taiga/userstorage/permissions.py | 6 +++--- taiga/userstorage/serializers.py | 6 +++--- taiga/webhooks/__init__.py | 6 +++--- taiga/webhooks/api.py | 6 +++--- taiga/webhooks/apps.py | 6 +++--- taiga/webhooks/models.py | 6 +++--- taiga/webhooks/permissions.py | 6 +++--- taiga/webhooks/serializers.py | 6 +++--- taiga/webhooks/signal_handlers.py | 6 +++--- taiga/webhooks/tasks.py | 4 ++-- tests/conftest.py | 8 ++++---- tests/factories.py | 8 ++++---- tests/fixtures.py | 8 ++++---- .../test_issues_custom_attributes_resource.py | 6 +++--- .../test_tasks_custom_attributes_resource.py | 6 +++--- .../test_userstories_custom_attributes_resource.py | 6 +++--- tests/integration/test_auth_api.py | 8 ++++---- tests/integration/test_custom_attributes_issues.py | 6 +++--- tests/integration/test_custom_attributes_tasks.py | 6 +++--- tests/integration/test_custom_attributes_user_stories.py | 6 +++--- tests/integration/test_exporter_api.py | 6 +++--- tests/integration/test_history.py | 8 ++++---- tests/integration/test_importer_api.py | 6 +++--- tests/integration/test_mdrender.py | 8 ++++---- tests/integration/test_milestones.py | 8 ++++---- tests/integration/test_neighbors.py | 8 ++++---- tests/integration/test_notifications.py | 8 ++++---- tests/integration/test_occ.py | 6 +++--- tests/integration/test_references_sequences.py | 8 ++++---- tests/integration/test_roles.py | 8 ++++---- tests/integration/test_searches.py | 8 ++++---- tests/integration/test_throwttling.py | 6 +++--- tests/integration/test_timeline.py | 8 ++++---- tests/integration/test_us_autoclosing.py | 8 ++++---- tests/integration/test_userstorage_api.py | 8 ++++---- tests/integration/test_vote_issues.py | 8 ++++---- tests/integration/test_vote_tasks.py | 8 ++++---- tests/integration/test_vote_userstories.py | 8 ++++---- tests/integration/test_votes.py | 8 ++++---- tests/integration/test_watch_issues.py | 8 ++++---- tests/integration/test_watch_milestones.py | 8 ++++---- tests/integration/test_watch_projects.py | 8 ++++---- tests/integration/test_watch_tasks.py | 8 ++++---- tests/integration/test_watch_userstories.py | 8 ++++---- tests/integration/test_watch_wikipages.py | 8 ++++---- tests/integration/test_webhooks.py | 8 ++++---- tests/models.py | 8 ++++---- tests/unit/conftest.py | 8 ++++---- tests/unit/test_base_api_permissions.py | 6 +++--- tests/unit/test_deferred.py | 6 +++--- tests/unit/test_export.py | 6 +++--- tests/unit/test_gravatar.py | 6 +++--- tests/unit/test_mdrender.py | 8 ++++---- tests/unit/test_permissions.py | 6 +++--- tests/unit/test_slug.py | 8 ++++---- tests/unit/test_timeline.py | 8 ++++---- tests/unit/test_tokens.py | 8 ++++---- tests/unit/test_utils.py | 6 +++--- tests/utils.py | 8 ++++---- 372 files changed, 1103 insertions(+), 1103 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 8fec513c..46770d5c 100755 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/__init__.py b/settings/__init__.py index dd336c53..fb18d127 100644 --- a/settings/__init__.py +++ b/settings/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/celery.py b/settings/celery.py index 59c8d51a..70cd1095 100644 --- a/settings/celery.py +++ b/settings/celery.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/common.py b/settings/common.py index 37fb1e27..544758d3 100644 --- a/settings/common.py +++ b/settings/common.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/development.py b/settings/development.py index 950ddd92..1a77df9d 100644 --- a/settings/development.py +++ b/settings/development.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/local.py.example b/settings/local.py.example index a6d16fab..b6bcf2b9 100644 --- a/settings/local.py.example +++ b/settings/local.py.example @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/sr.py b/settings/sr.py index cd1bc113..9c523878 100644 --- a/settings/sr.py +++ b/settings/sr.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/testing.py b/settings/testing.py index 9fb6ec74..01eff7c5 100644 --- a/settings/testing.py +++ b/settings/testing.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/settings/travis.py b/settings/travis.py index 6f27652f..20920546 100644 --- a/settings/travis.py +++ b/settings/travis.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/__init__.py b/taiga/__init__.py index b62e0bd0..721fcd42 100644 --- a/taiga/__init__.py +++ b/taiga/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/api.py b/taiga/auth/api.py index a666dc2e..c70e7e7f 100644 --- a/taiga/auth/api.py +++ b/taiga/auth/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/backends.py b/taiga/auth/backends.py index fe44544b..d2f71553 100644 --- a/taiga/auth/backends.py +++ b/taiga/auth/backends.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/permissions.py b/taiga/auth/permissions.py index c0e457b3..7fe0d452 100644 --- a/taiga/auth/permissions.py +++ b/taiga/auth/permissions.py @@ -1,5 +1,5 @@ -# Copyright (C) 2014 Andrey Antukh # Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh # Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/serializers.py b/taiga/auth/serializers.py index 2073e000..42b077e7 100644 --- a/taiga/auth/serializers.py +++ b/taiga/auth/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/services.py b/taiga/auth/services.py index 55277a8f..73b65b5d 100644 --- a/taiga/auth/services.py +++ b/taiga/auth/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/signals.py b/taiga/auth/signals.py index 9c5b9ca0..2f674fe1 100644 --- a/taiga/auth/signals.py +++ b/taiga/auth/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/auth/tokens.py b/taiga/auth/tokens.py index f113ba8a..a24b91e4 100644 --- a/taiga/auth/tokens.py +++ b/taiga/auth/tokens.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/__init__.py b/taiga/base/__init__.py index d6006178..6aa21c11 100644 --- a/taiga/base/__init__.py +++ b/taiga/base/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/__init__.py b/taiga/base/api/__init__.py index 8b2b6ad6..f8457cd1 100644 --- a/taiga/base/api/__init__.py +++ b/taiga/base/api/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/authentication.py b/taiga/base/api/authentication.py index 8343fa30..ad83bdbf 100644 --- a/taiga/base/api/authentication.py +++ b/taiga/base/api/authentication.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/fields.py b/taiga/base/api/fields.py index 942878f7..ad05e422 100644 --- a/taiga/base/api/fields.py +++ b/taiga/base/api/fields.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/generics.py b/taiga/base/api/generics.py index de834139..2315bfc1 100644 --- a/taiga/base/api/generics.py +++ b/taiga/base/api/generics.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/mixins.py b/taiga/base/api/mixins.py index 5576db90..371b44c7 100644 --- a/taiga/base/api/mixins.py +++ b/taiga/base/api/mixins.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/negotiation.py b/taiga/base/api/negotiation.py index 60278752..f4984a11 100644 --- a/taiga/base/api/negotiation.py +++ b/taiga/base/api/negotiation.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/pagination.py b/taiga/base/api/pagination.py index 028d8106..dbab110b 100644 --- a/taiga/base/api/pagination.py +++ b/taiga/base/api/pagination.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/parsers.py b/taiga/base/api/parsers.py index 1465f601..3b254633 100644 --- a/taiga/base/api/parsers.py +++ b/taiga/base/api/parsers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/permissions.py b/taiga/base/api/permissions.py index c4e6917d..54e3be02 100644 --- a/taiga/base/api/permissions.py +++ b/taiga/base/api/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/relations.py b/taiga/base/api/relations.py index 87fbfb4c..4c02ec91 100644 --- a/taiga/base/api/relations.py +++ b/taiga/base/api/relations.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/renderers.py b/taiga/base/api/renderers.py index 2da7ed4c..c30cb074 100644 --- a/taiga/base/api/renderers.py +++ b/taiga/base/api/renderers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/request.py b/taiga/base/api/request.py index c57d29fc..0e5fe48a 100644 --- a/taiga/base/api/request.py +++ b/taiga/base/api/request.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/reverse.py b/taiga/base/api/reverse.py index 71c7c047..1049388f 100644 --- a/taiga/base/api/reverse.py +++ b/taiga/base/api/reverse.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/serializers.py b/taiga/base/api/serializers.py index 9a52e873..3f33e180 100644 --- a/taiga/base/api/serializers.py +++ b/taiga/base/api/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/settings.py b/taiga/base/api/settings.py index 34dd9717..5eda4866 100644 --- a/taiga/base/api/settings.py +++ b/taiga/base/api/settings.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/static/api/css/bootstrap-tweaks.css b/taiga/base/api/static/api/css/bootstrap-tweaks.css index b1cd265b..98b0348f 100644 --- a/taiga/base/api/static/api/css/bootstrap-tweaks.css +++ b/taiga/base/api/static/api/css/bootstrap-tweaks.css @@ -1,7 +1,7 @@ /* - * Copyright (C) 2015 Andrey Antukh - * Copyright (C) 2015 Jesús Espino - * Copyright (C) 2015 David Barragán + * Copyright (C) 2014-2015 Andrey Antukh + * Copyright (C) 2014-2015 Jesús Espino + * Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/static/api/css/default.css b/taiga/base/api/static/api/css/default.css index d7c5722e..cbd7191c 100644 --- a/taiga/base/api/static/api/css/default.css +++ b/taiga/base/api/static/api/css/default.css @@ -1,7 +1,7 @@ /* - * Copyright (C) 2015 Andrey Antukh - * Copyright (C) 2015 Jesús Espino - * Copyright (C) 2015 David Barragán + * Copyright (C) 2014-2015 Andrey Antukh + * Copyright (C) 2014-2015 Jesús Espino + * Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/static/api/css/prettify.css b/taiga/base/api/static/api/css/prettify.css index c037439d..1511794c 100644 --- a/taiga/base/api/static/api/css/prettify.css +++ b/taiga/base/api/static/api/css/prettify.css @@ -1,7 +1,7 @@ /* - * Copyright (C) 2015 Andrey Antukh - * Copyright (C) 2015 Jesús Espino - * Copyright (C) 2015 David Barragán + * Copyright (C) 2014-2015 Andrey Antukh + * Copyright (C) 2014-2015 Jesús Espino + * Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/static/api/js/default.js b/taiga/base/api/static/api/js/default.js index 3e9b9e3c..fc8563d2 100644 --- a/taiga/base/api/static/api/js/default.js +++ b/taiga/base/api/static/api/js/default.js @@ -1,7 +1,7 @@ /* - * Copyright (C) 2015 Andrey Antukh - * Copyright (C) 2015 Jesús Espino - * Copyright (C) 2015 David Barragán + * Copyright (C) 2014-2015 Andrey Antukh + * Copyright (C) 2014-2015 Jesús Espino + * Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/static/api/js/prettify-min.js b/taiga/base/api/static/api/js/prettify-min.js index 54d16bac..c225269f 100644 --- a/taiga/base/api/static/api/js/prettify-min.js +++ b/taiga/base/api/static/api/js/prettify-min.js @@ -1,7 +1,7 @@ /* - * Copyright (C) 2015 Andrey Antukh - * Copyright (C) 2015 Jesús Espino - * Copyright (C) 2015 David Barragán + * Copyright (C) 2014-2015 Andrey Antukh + * Copyright (C) 2014-2015 Jesús Espino + * Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/templatetags/api.py b/taiga/base/api/templatetags/api.py index 987cb90b..642b476d 100644 --- a/taiga/base/api/templatetags/api.py +++ b/taiga/base/api/templatetags/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/throttling.py b/taiga/base/api/throttling.py index bec43b87..89cf4c7d 100644 --- a/taiga/base/api/throttling.py +++ b/taiga/base/api/throttling.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/urlpatterns.py b/taiga/base/api/urlpatterns.py index a48fa572..4c67ad23 100644 --- a/taiga/base/api/urlpatterns.py +++ b/taiga/base/api/urlpatterns.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/urls.py b/taiga/base/api/urls.py index 0e1f61bf..e7e4af2b 100644 --- a/taiga/base/api/urls.py +++ b/taiga/base/api/urls.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils.py b/taiga/base/api/utils.py index 433e668a..8f803b0a 100644 --- a/taiga/base/api/utils.py +++ b/taiga/base/api/utils.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils/__init__.py b/taiga/base/api/utils/__init__.py index d215bf8f..a555ba83 100644 --- a/taiga/base/api/utils/__init__.py +++ b/taiga/base/api/utils/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils/breadcrumbs.py b/taiga/base/api/utils/breadcrumbs.py index 39fa383f..950fe710 100644 --- a/taiga/base/api/utils/breadcrumbs.py +++ b/taiga/base/api/utils/breadcrumbs.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils/encoders.py b/taiga/base/api/utils/encoders.py index 29dc36d3..cc998d24 100644 --- a/taiga/base/api/utils/encoders.py +++ b/taiga/base/api/utils/encoders.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils/formatting.py b/taiga/base/api/utils/formatting.py index c9940975..c8a1781e 100644 --- a/taiga/base/api/utils/formatting.py +++ b/taiga/base/api/utils/formatting.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/utils/mediatypes.py b/taiga/base/api/utils/mediatypes.py index dce7b06a..b049f166 100644 --- a/taiga/base/api/utils/mediatypes.py +++ b/taiga/base/api/utils/mediatypes.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/views.py b/taiga/base/api/views.py index 293053da..0d5a60da 100644 --- a/taiga/base/api/views.py +++ b/taiga/base/api/views.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/api/viewsets.py b/taiga/base/api/viewsets.py index e587d4ab..9122972a 100644 --- a/taiga/base/api/viewsets.py +++ b/taiga/base/api/viewsets.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/apps.py b/taiga/base/apps.py index 7125cbe7..32ef371c 100644 --- a/taiga/base/apps.py +++ b/taiga/base/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/connectors/exceptions.py b/taiga/base/connectors/exceptions.py index 1619abba..7173c757 100644 --- a/taiga/base/connectors/exceptions.py +++ b/taiga/base/connectors/exceptions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/decorators.py b/taiga/base/decorators.py index d11b97f9..37912397 100644 --- a/taiga/base/decorators.py +++ b/taiga/base/decorators.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/exceptions.py b/taiga/base/exceptions.py index 7adb31f9..a6e3850f 100644 --- a/taiga/base/exceptions.py +++ b/taiga/base/exceptions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/fields.py b/taiga/base/fields.py index 74b27c10..0c5d96aa 100644 --- a/taiga/base/fields.py +++ b/taiga/base/fields.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/filters.py b/taiga/base/filters.py index d662b47d..a6cf64e3 100644 --- a/taiga/base/filters.py +++ b/taiga/base/filters.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/formats/en/formats.py b/taiga/base/formats/en/formats.py index 959dba6d..6f5a83d4 100644 --- a/taiga/base/formats/en/formats.py +++ b/taiga/base/formats/en/formats.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/formats/es/formats.py b/taiga/base/formats/es/formats.py index 8fc0d277..a6c8e8f5 100644 --- a/taiga/base/formats/es/formats.py +++ b/taiga/base/formats/es/formats.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/mails.py b/taiga/base/mails.py index b078cbcf..ac0517e0 100644 --- a/taiga/base/mails.py +++ b/taiga/base/mails.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/management/commands/test_emails.py b/taiga/base/management/commands/test_emails.py index e0cd5e94..c6d20a7d 100644 --- a/taiga/base/management/commands/test_emails.py +++ b/taiga/base/management/commands/test_emails.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/middleware/cors.py b/taiga/base/middleware/cors.py index 3d6aed10..b4ed438f 100644 --- a/taiga/base/middleware/cors.py +++ b/taiga/base/middleware/cors.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/neighbors.py b/taiga/base/neighbors.py index 545c130c..0a23f2d6 100644 --- a/taiga/base/neighbors.py +++ b/taiga/base/neighbors.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/response.py b/taiga/base/response.py index 47fc6641..458411c7 100644 --- a/taiga/base/response.py +++ b/taiga/base/response.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/routers.py b/taiga/base/routers.py index c792771c..6b72826c 100644 --- a/taiga/base/routers.py +++ b/taiga/base/routers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/status.py b/taiga/base/status.py index 5fb0a4cf..0000145c 100644 --- a/taiga/base/status.py +++ b/taiga/base/status.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/storage.py b/taiga/base/storage.py index 35e234df..ad79c9fd 100644 --- a/taiga/base/storage.py +++ b/taiga/base/storage.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/tags.py b/taiga/base/tags.py index 4b7c6409..9af3cfe6 100644 --- a/taiga/base/tags.py +++ b/taiga/base/tags.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/throttling.py b/taiga/base/throttling.py index 268a397d..edc1fa14 100644 --- a/taiga/base/throttling.py +++ b/taiga/base/throttling.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/contenttypes.py b/taiga/base/utils/contenttypes.py index 1a8aabcf..a475b352 100644 --- a/taiga/base/utils/contenttypes.py +++ b/taiga/base/utils/contenttypes.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/db.py b/taiga/base/utils/db.py index 2762833e..f71b10f3 100644 --- a/taiga/base/utils/db.py +++ b/taiga/base/utils/db.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/dicts.py b/taiga/base/utils/dicts.py index 512a044d..4a3c1ce6 100644 --- a/taiga/base/utils/dicts.py +++ b/taiga/base/utils/dicts.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/diff.py b/taiga/base/utils/diff.py index 7c8ea034..27f1281e 100644 --- a/taiga/base/utils/diff.py +++ b/taiga/base/utils/diff.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/functions.py b/taiga/base/utils/functions.py index d20f824a..3570a59a 100644 --- a/taiga/base/utils/functions.py +++ b/taiga/base/utils/functions.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/utils/iterators.py b/taiga/base/utils/iterators.py index fff3a9de..f7249112 100644 --- a/taiga/base/utils/iterators.py +++ b/taiga/base/utils/iterators.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/utils/json.py b/taiga/base/utils/json.py index 40132b34..d9e54132 100644 --- a/taiga/base/utils/json.py +++ b/taiga/base/utils/json.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/sequence.py b/taiga/base/utils/sequence.py index 18af3e2f..da50953a 100644 --- a/taiga/base/utils/sequence.py +++ b/taiga/base/utils/sequence.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/signals.py b/taiga/base/utils/signals.py index 1fe9c071..d2700790 100644 --- a/taiga/base/utils/signals.py +++ b/taiga/base/utils/signals.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/base/utils/slug.py b/taiga/base/utils/slug.py index 03b95767..48776ac3 100644 --- a/taiga/base/utils/slug.py +++ b/taiga/base/utils/slug.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/text.py b/taiga/base/utils/text.py index df9b0259..b8de6aef 100644 --- a/taiga/base/utils/text.py +++ b/taiga/base/utils/text.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/base/utils/urls.py b/taiga/base/utils/urls.py index 2cd5f067..e13d783e 100644 --- a/taiga/base/utils/urls.py +++ b/taiga/base/utils/urls.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/celery.py b/taiga/celery.py index ef9b7d06..8084290b 100644 --- a/taiga/celery.py +++ b/taiga/celery.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/contrib_routers.py b/taiga/contrib_routers.py index 311f96c3..129d56b6 100644 --- a/taiga/contrib_routers.py +++ b/taiga/contrib_routers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/deferred.py b/taiga/deferred.py index 62080a77..084a16f0 100644 --- a/taiga/deferred.py +++ b/taiga/deferred.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/events/__init__.py b/taiga/events/__init__.py index bc6d8fa2..b2d6c236 100644 --- a/taiga/events/__init__.py +++ b/taiga/events/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/events/apps.py b/taiga/events/apps.py index 40b51834..1081d6db 100644 --- a/taiga/events/apps.py +++ b/taiga/events/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/events/backends/__init__.py b/taiga/events/backends/__init__.py index f72b8c5b..da0d1ba3 100644 --- a/taiga/events/backends/__init__.py +++ b/taiga/events/backends/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/events/backends/base.py b/taiga/events/backends/base.py index 4eefcb55..16189070 100644 --- a/taiga/events/backends/base.py +++ b/taiga/events/backends/base.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/events/backends/postgresql.py b/taiga/events/backends/postgresql.py index 696a0813..beaf04ee 100644 --- a/taiga/events/backends/postgresql.py +++ b/taiga/events/backends/postgresql.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/events/backends/rabbitmq.py b/taiga/events/backends/rabbitmq.py index 182d2548..18b573b1 100644 --- a/taiga/events/backends/rabbitmq.py +++ b/taiga/events/backends/rabbitmq.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/events/events.py b/taiga/events/events.py index 26343694..3bd29173 100644 --- a/taiga/events/events.py +++ b/taiga/events/events.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/events/middleware.py b/taiga/events/middleware.py index 9dbfb103..6fdbe3ef 100644 --- a/taiga/events/middleware.py +++ b/taiga/events/middleware.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/events/signal_handlers.py b/taiga/events/signal_handlers.py index 33b00e89..e50b0f4c 100644 --- a/taiga/events/signal_handlers.py +++ b/taiga/events/signal_handlers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/api.py b/taiga/export_import/api.py index 6fabb96d..e9da52c0 100644 --- a/taiga/export_import/api.py +++ b/taiga/export_import/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/dump_service.py b/taiga/export_import/dump_service.py index 013e93e9..e09783a9 100644 --- a/taiga/export_import/dump_service.py +++ b/taiga/export_import/dump_service.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/management/commands/dump_project.py b/taiga/export_import/management/commands/dump_project.py index 9728d01c..6126a04a 100644 --- a/taiga/export_import/management/commands/dump_project.py +++ b/taiga/export_import/management/commands/dump_project.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/management/commands/load_dump.py b/taiga/export_import/management/commands/load_dump.py index 14016c6e..5afee1b8 100644 --- a/taiga/export_import/management/commands/load_dump.py +++ b/taiga/export_import/management/commands/load_dump.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/mixins.py b/taiga/export_import/mixins.py index bc5504fa..89a625e6 100644 --- a/taiga/export_import/mixins.py +++ b/taiga/export_import/mixins.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/permissions.py b/taiga/export_import/permissions.py index 2f63d272..23516de9 100644 --- a/taiga/export_import/permissions.py +++ b/taiga/export_import/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/renderers.py b/taiga/export_import/renderers.py index 30bf8d5f..7f7a2a28 100644 --- a/taiga/export_import/renderers.py +++ b/taiga/export_import/renderers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/serializers.py b/taiga/export_import/serializers.py index 02334510..7b329e60 100644 --- a/taiga/export_import/serializers.py +++ b/taiga/export_import/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/service.py b/taiga/export_import/service.py index ef0b8a22..27d88d33 100644 --- a/taiga/export_import/service.py +++ b/taiga/export_import/service.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/tasks.py b/taiga/export_import/tasks.py index 70a45f91..a33d2518 100644 --- a/taiga/export_import/tasks.py +++ b/taiga/export_import/tasks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/export_import/throttling.py b/taiga/export_import/throttling.py index a59d7e33..8a772520 100644 --- a/taiga/export_import/throttling.py +++ b/taiga/export_import/throttling.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/admin.py b/taiga/external_apps/admin.py index 62efaafd..c1efa854 100644 --- a/taiga/external_apps/admin.py +++ b/taiga/external_apps/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/api.py b/taiga/external_apps/api.py index 092a9dfd..8da1ca9a 100644 --- a/taiga/external_apps/api.py +++ b/taiga/external_apps/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/auth_backends.py b/taiga/external_apps/auth_backends.py index ff86e70e..6ab025bd 100644 --- a/taiga/external_apps/auth_backends.py +++ b/taiga/external_apps/auth_backends.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/encryption.py b/taiga/external_apps/encryption.py index 1df1bda7..523c49d1 100644 --- a/taiga/external_apps/encryption.py +++ b/taiga/external_apps/encryption.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/models.py b/taiga/external_apps/models.py index 29eb981c..b1ffed26 100644 --- a/taiga/external_apps/models.py +++ b/taiga/external_apps/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/permissions.py b/taiga/external_apps/permissions.py index 88604e23..2132ea2a 100644 --- a/taiga/external_apps/permissions.py +++ b/taiga/external_apps/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/serializers.py b/taiga/external_apps/serializers.py index fe472d8c..bc3cc0fc 100644 --- a/taiga/external_apps/serializers.py +++ b/taiga/external_apps/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/external_apps/services.py b/taiga/external_apps/services.py index 14ae8b73..7e1f78a4 100644 --- a/taiga/external_apps/services.py +++ b/taiga/external_apps/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/__init__.py b/taiga/feedback/__init__.py index 17e45261..69fa6d10 100644 --- a/taiga/feedback/__init__.py +++ b/taiga/feedback/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/admin.py b/taiga/feedback/admin.py index 512abb16..0c6f5e0c 100644 --- a/taiga/feedback/admin.py +++ b/taiga/feedback/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/api.py b/taiga/feedback/api.py index c0efb23d..46dc31bd 100644 --- a/taiga/feedback/api.py +++ b/taiga/feedback/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/apps.py b/taiga/feedback/apps.py index 7ae2c1af..8d8ec510 100644 --- a/taiga/feedback/apps.py +++ b/taiga/feedback/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/models.py b/taiga/feedback/models.py index a56de2b9..f60aee92 100644 --- a/taiga/feedback/models.py +++ b/taiga/feedback/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/permissions.py b/taiga/feedback/permissions.py index 6b755975..bbb53bdb 100644 --- a/taiga/feedback/permissions.py +++ b/taiga/feedback/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/routers.py b/taiga/feedback/routers.py index a3486b52..06d8988b 100644 --- a/taiga/feedback/routers.py +++ b/taiga/feedback/routers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/serializers.py b/taiga/feedback/serializers.py index 872bad90..647c0e96 100644 --- a/taiga/feedback/serializers.py +++ b/taiga/feedback/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/feedback/services.py b/taiga/feedback/services.py index a0f16762..6fee5c79 100644 --- a/taiga/feedback/services.py +++ b/taiga/feedback/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/front/sitemaps/__init__.py b/taiga/front/sitemaps/__init__.py index ba5da7bd..e07dd928 100644 --- a/taiga/front/sitemaps/__init__.py +++ b/taiga/front/sitemaps/__init__.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/base.py b/taiga/front/sitemaps/base.py index 83967f4d..418ed739 100644 --- a/taiga/front/sitemaps/base.py +++ b/taiga/front/sitemaps/base.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/generics.py b/taiga/front/sitemaps/generics.py index 27fbc075..41479a61 100644 --- a/taiga/front/sitemaps/generics.py +++ b/taiga/front/sitemaps/generics.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/issues.py b/taiga/front/sitemaps/issues.py index e4404138..912712fc 100644 --- a/taiga/front/sitemaps/issues.py +++ b/taiga/front/sitemaps/issues.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/milestones.py b/taiga/front/sitemaps/milestones.py index 7dde324b..049d3c9c 100644 --- a/taiga/front/sitemaps/milestones.py +++ b/taiga/front/sitemaps/milestones.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/projects.py b/taiga/front/sitemaps/projects.py index f9ad82f8..fc56adca 100644 --- a/taiga/front/sitemaps/projects.py +++ b/taiga/front/sitemaps/projects.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/tasks.py b/taiga/front/sitemaps/tasks.py index 264be4de..fa066a3b 100644 --- a/taiga/front/sitemaps/tasks.py +++ b/taiga/front/sitemaps/tasks.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/users.py b/taiga/front/sitemaps/users.py index c29420e0..0cd2c2ed 100644 --- a/taiga/front/sitemaps/users.py +++ b/taiga/front/sitemaps/users.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/userstories.py b/taiga/front/sitemaps/userstories.py index da16d19b..669db7ed 100644 --- a/taiga/front/sitemaps/userstories.py +++ b/taiga/front/sitemaps/userstories.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/sitemaps/wiki.py b/taiga/front/sitemaps/wiki.py index eeb96a58..33c0cf0e 100644 --- a/taiga/front/sitemaps/wiki.py +++ b/taiga/front/sitemaps/wiki.py @@ -1,5 +1,5 @@ -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Taiga Agile LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as diff --git a/taiga/front/templatetags/functions.py b/taiga/front/templatetags/functions.py index 3391fece..1c2fdaea 100644 --- a/taiga/front/templatetags/functions.py +++ b/taiga/front/templatetags/functions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/front/urls.py b/taiga/front/urls.py index b377f655..5987e5af 100644 --- a/taiga/front/urls.py +++ b/taiga/front/urls.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/api.py b/taiga/hooks/api.py index d4345cbc..d807f19d 100644 --- a/taiga/hooks/api.py +++ b/taiga/hooks/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/bitbucket/api.py b/taiga/hooks/bitbucket/api.py index 0b304ac8..781eca96 100644 --- a/taiga/hooks/bitbucket/api.py +++ b/taiga/hooks/bitbucket/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/bitbucket/event_hooks.py b/taiga/hooks/bitbucket/event_hooks.py index 73008a3e..3ee7c92b 100644 --- a/taiga/hooks/bitbucket/event_hooks.py +++ b/taiga/hooks/bitbucket/event_hooks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/bitbucket/services.py b/taiga/hooks/bitbucket/services.py index ddd4af79..2bb9e574 100644 --- a/taiga/hooks/bitbucket/services.py +++ b/taiga/hooks/bitbucket/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/event_hooks.py b/taiga/hooks/event_hooks.py index 0d26be38..eebc45a0 100644 --- a/taiga/hooks/event_hooks.py +++ b/taiga/hooks/event_hooks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/exceptions.py b/taiga/hooks/exceptions.py index 697674d4..1a214ab0 100644 --- a/taiga/hooks/exceptions.py +++ b/taiga/hooks/exceptions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/github/api.py b/taiga/hooks/github/api.py index 8251858f..9082a2e6 100644 --- a/taiga/hooks/github/api.py +++ b/taiga/hooks/github/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/github/event_hooks.py b/taiga/hooks/github/event_hooks.py index 3dbd0417..20bbf11c 100644 --- a/taiga/hooks/github/event_hooks.py +++ b/taiga/hooks/github/event_hooks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/github/services.py b/taiga/hooks/github/services.py index bc16c380..cbd63ef6 100644 --- a/taiga/hooks/github/services.py +++ b/taiga/hooks/github/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/gitlab/api.py b/taiga/hooks/gitlab/api.py index 03ae3a06..4cc71fd9 100644 --- a/taiga/hooks/gitlab/api.py +++ b/taiga/hooks/gitlab/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/gitlab/event_hooks.py b/taiga/hooks/gitlab/event_hooks.py index 83760d25..4c1f7dd3 100644 --- a/taiga/hooks/gitlab/event_hooks.py +++ b/taiga/hooks/gitlab/event_hooks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/hooks/gitlab/services.py b/taiga/hooks/gitlab/services.py index 2d99969a..c7bfe5b9 100644 --- a/taiga/hooks/gitlab/services.py +++ b/taiga/hooks/gitlab/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/locale/api.py b/taiga/locale/api.py index 9a35be0b..03f11d56 100644 --- a/taiga/locale/api.py +++ b/taiga/locale/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 364fd824..8422123a 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index d64b20e4..a7fd30ca 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 785176f5..f3f60e36 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # FIRST AUTHOR , YEAR. # diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index d779fa5c..d8536c66 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 8c0149b5..13df40c5 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 59d43e20..2f77501a 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index 7ce2d8bb..51da3973 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 1f1b87f4..8c7e29a7 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/permissions.py b/taiga/locale/permissions.py index 406a4285..1c14c352 100644 --- a/taiga/locale/permissions.py +++ b/taiga/locale/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 330d4285..bbc893c3 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 1f252bea..6b644639 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index dabfc39a..d79db4bb 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 089f07cd..1bf09ea4 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -1,5 +1,5 @@ # taiga-back.taiga. -# Copyright (C) 2015 Taiga Dev Team +# Copyright (C) 2014-2015 Taiga Dev Team # This file is distributed under the same license as the taiga-back package. # # Translators: diff --git a/taiga/mdrender/__init__.py b/taiga/mdrender/__init__.py index 8cef3795..abec3c69 100644 --- a/taiga/mdrender/__init__.py +++ b/taiga/mdrender/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/mdrender/extensions/target_link.py b/taiga/mdrender/extensions/target_link.py index 992399ea..7754d94b 100644 --- a/taiga/mdrender/extensions/target_link.py +++ b/taiga/mdrender/extensions/target_link.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Alejandro Alonso +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Alejandro Alonso # 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 diff --git a/taiga/mdrender/extensions/wikilinks.py b/taiga/mdrender/extensions/wikilinks.py index 1fd703b3..9d106d82 100644 --- a/taiga/mdrender/extensions/wikilinks.py +++ b/taiga/mdrender/extensions/wikilinks.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/mdrender/service.py b/taiga/mdrender/service.py index a507b1c2..326f4b2c 100644 --- a/taiga/mdrender/service.py +++ b/taiga/mdrender/service.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/mdrender/templatetags/functions.py b/taiga/mdrender/templatetags/functions.py index 88c7af86..fc15c39f 100644 --- a/taiga/mdrender/templatetags/functions.py +++ b/taiga/mdrender/templatetags/functions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/permissions/permissions.py b/taiga/permissions/permissions.py index 37cd32de..7761abbf 100644 --- a/taiga/permissions/permissions.py +++ b/taiga/permissions/permissions.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/permissions/service.py b/taiga/permissions/service.py index 3a79b6ee..90ed3050 100644 --- a/taiga/permissions/service.py +++ b/taiga/permissions/service.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/__init__.py b/taiga/projects/__init__.py index 48278e73..c8c59bf3 100644 --- a/taiga/projects/__init__.py +++ b/taiga/projects/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/admin.py b/taiga/projects/admin.py index 5dcc9758..0dee8cde 100644 --- a/taiga/projects/admin.py +++ b/taiga/projects/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 78993322..efec71c1 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/apps.py b/taiga/projects/apps.py index 06b42de8..07938210 100644 --- a/taiga/projects/apps.py +++ b/taiga/projects/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/__init__.py b/taiga/projects/attachments/__init__.py index fcc59d48..17882254 100644 --- a/taiga/projects/attachments/__init__.py +++ b/taiga/projects/attachments/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/admin.py b/taiga/projects/attachments/admin.py index e8d78de6..c30b4f52 100644 --- a/taiga/projects/attachments/admin.py +++ b/taiga/projects/attachments/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/api.py b/taiga/projects/attachments/api.py index a369a5cb..0a40f8b7 100644 --- a/taiga/projects/attachments/api.py +++ b/taiga/projects/attachments/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/apps.py b/taiga/projects/attachments/apps.py index a4497463..c52458b9 100644 --- a/taiga/projects/attachments/apps.py +++ b/taiga/projects/attachments/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/models.py b/taiga/projects/attachments/models.py index 30fa5c22..f5c089fa 100644 --- a/taiga/projects/attachments/models.py +++ b/taiga/projects/attachments/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/permissions.py b/taiga/projects/attachments/permissions.py index f709c378..603d4f98 100644 --- a/taiga/projects/attachments/permissions.py +++ b/taiga/projects/attachments/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/attachments/serializers.py b/taiga/projects/attachments/serializers.py index f255fa8e..549acabd 100644 --- a/taiga/projects/attachments/serializers.py +++ b/taiga/projects/attachments/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/choices.py b/taiga/projects/choices.py index 4cf2c843..0e443847 100644 --- a/taiga/projects/choices.py +++ b/taiga/projects/choices.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/admin.py b/taiga/projects/custom_attributes/admin.py index 201a31f0..fe0e3c6b 100644 --- a/taiga/projects/custom_attributes/admin.py +++ b/taiga/projects/custom_attributes/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/api.py b/taiga/projects/custom_attributes/api.py index c93bb790..bb490d4e 100644 --- a/taiga/projects/custom_attributes/api.py +++ b/taiga/projects/custom_attributes/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index 6d05121c..e75a34e1 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/permissions.py b/taiga/projects/custom_attributes/permissions.py index 14307d1a..7a780a57 100644 --- a/taiga/projects/custom_attributes/permissions.py +++ b/taiga/projects/custom_attributes/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/serializers.py b/taiga/projects/custom_attributes/serializers.py index 71a5ff5f..4b82a189 100644 --- a/taiga/projects/custom_attributes/serializers.py +++ b/taiga/projects/custom_attributes/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/services.py b/taiga/projects/custom_attributes/services.py index 7cbea6c4..1e5795cb 100644 --- a/taiga/projects/custom_attributes/services.py +++ b/taiga/projects/custom_attributes/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/custom_attributes/signals.py b/taiga/projects/custom_attributes/signals.py index fa90bb10..1e0e96e3 100644 --- a/taiga/projects/custom_attributes/signals.py +++ b/taiga/projects/custom_attributes/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/api.py b/taiga/projects/history/api.py index 2d6c365b..9e7bef23 100644 --- a/taiga/projects/history/api.py +++ b/taiga/projects/history/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/choices.py b/taiga/projects/history/choices.py index 0895ca8c..1c38af8f 100644 --- a/taiga/projects/history/choices.py +++ b/taiga/projects/history/choices.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/projects/history/freeze_impl.py b/taiga/projects/history/freeze_impl.py index 77a81423..a51d3011 100644 --- a/taiga/projects/history/freeze_impl.py +++ b/taiga/projects/history/freeze_impl.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/mixins.py b/taiga/projects/history/mixins.py index 45a7c97e..27fa7632 100644 --- a/taiga/projects/history/mixins.py +++ b/taiga/projects/history/mixins.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/models.py b/taiga/projects/history/models.py index 4cfcaccc..cea42ae4 100644 --- a/taiga/projects/history/models.py +++ b/taiga/projects/history/models.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/projects/history/permissions.py b/taiga/projects/history/permissions.py index c0adf8d6..636fe6a9 100644 --- a/taiga/projects/history/permissions.py +++ b/taiga/projects/history/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/serializers.py b/taiga/projects/history/serializers.py index ac6cf0d2..eab06fa7 100644 --- a/taiga/projects/history/serializers.py +++ b/taiga/projects/history/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/history/services.py b/taiga/projects/history/services.py index 317a9374..a199f7ad 100644 --- a/taiga/projects/history/services.py +++ b/taiga/projects/history/services.py @@ -1,4 +1,4 @@ -# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014-2015 Andrey Antukh # 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 diff --git a/taiga/projects/history/templatetags/functions.py b/taiga/projects/history/templatetags/functions.py index eadda2c6..b5118c3a 100644 --- a/taiga/projects/history/templatetags/functions.py +++ b/taiga/projects/history/templatetags/functions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/__init__.py b/taiga/projects/issues/__init__.py index aff90c37..b6be563e 100644 --- a/taiga/projects/issues/__init__.py +++ b/taiga/projects/issues/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/admin.py b/taiga/projects/issues/admin.py index fe891719..7da02b5b 100644 --- a/taiga/projects/issues/admin.py +++ b/taiga/projects/issues/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/api.py b/taiga/projects/issues/api.py index e187c3df..1df9875e 100644 --- a/taiga/projects/issues/api.py +++ b/taiga/projects/issues/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/apps.py b/taiga/projects/issues/apps.py index cc68fb73..485513f0 100644 --- a/taiga/projects/issues/apps.py +++ b/taiga/projects/issues/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/models.py b/taiga/projects/issues/models.py index 943509b3..81745d4a 100644 --- a/taiga/projects/issues/models.py +++ b/taiga/projects/issues/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/permissions.py b/taiga/projects/issues/permissions.py index 91f988ca..8c8ddd4d 100644 --- a/taiga/projects/issues/permissions.py +++ b/taiga/projects/issues/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/serializers.py b/taiga/projects/issues/serializers.py index f7978861..fac4ba30 100644 --- a/taiga/projects/issues/serializers.py +++ b/taiga/projects/issues/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/services.py b/taiga/projects/issues/services.py index d1156227..2929aef1 100644 --- a/taiga/projects/issues/services.py +++ b/taiga/projects/issues/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/issues/signals.py b/taiga/projects/issues/signals.py index 9389d410..1a50a911 100644 --- a/taiga/projects/issues/signals.py +++ b/taiga/projects/issues/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 566bfe16..84a941e1 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/admin.py b/taiga/projects/milestones/admin.py index 52919e87..378928ce 100644 --- a/taiga/projects/milestones/admin.py +++ b/taiga/projects/milestones/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/api.py b/taiga/projects/milestones/api.py index e2e62363..d9e044be 100644 --- a/taiga/projects/milestones/api.py +++ b/taiga/projects/milestones/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/models.py b/taiga/projects/milestones/models.py index 7e18048e..0e380c9b 100644 --- a/taiga/projects/milestones/models.py +++ b/taiga/projects/milestones/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/permissions.py b/taiga/projects/milestones/permissions.py index 843c0c8a..c088d9a9 100644 --- a/taiga/projects/milestones/permissions.py +++ b/taiga/projects/milestones/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/serializers.py b/taiga/projects/milestones/serializers.py index 471b9546..50e90a49 100644 --- a/taiga/projects/milestones/serializers.py +++ b/taiga/projects/milestones/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/milestones/services.py b/taiga/projects/milestones/services.py index a94be521..f852403f 100644 --- a/taiga/projects/milestones/services.py +++ b/taiga/projects/milestones/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/mixins/blocked.py b/taiga/projects/mixins/blocked.py index 43ce45e7..34db95b1 100644 --- a/taiga/projects/mixins/blocked.py +++ b/taiga/projects/mixins/blocked.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/mixins/on_destroy.py b/taiga/projects/mixins/on_destroy.py index 6fe69c7b..6ba1c40f 100644 --- a/taiga/projects/mixins/on_destroy.py +++ b/taiga/projects/mixins/on_destroy.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/mixins/ordering.py b/taiga/projects/mixins/ordering.py index b180bc1f..29723fd3 100644 --- a/taiga/projects/mixins/ordering.py +++ b/taiga/projects/mixins/ordering.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/models.py b/taiga/projects/models.py index fe89fac8..c0455e4b 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/admin.py b/taiga/projects/notifications/admin.py index 2dc22d61..a2278932 100644 --- a/taiga/projects/notifications/admin.py +++ b/taiga/projects/notifications/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py index d4b92c96..0d497be4 100644 --- a/taiga/projects/notifications/api.py +++ b/taiga/projects/notifications/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/choices.py b/taiga/projects/notifications/choices.py index 04e28d89..651d5909 100644 --- a/taiga/projects/notifications/choices.py +++ b/taiga/projects/notifications/choices.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/management/commands/send_notifications.py b/taiga/projects/notifications/management/commands/send_notifications.py index 4f2e4643..5aac7558 100644 --- a/taiga/projects/notifications/management/commands/send_notifications.py +++ b/taiga/projects/notifications/management/commands/send_notifications.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index fe1812f4..f62d1157 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/models.py b/taiga/projects/notifications/models.py index 6f224588..6ce2356b 100644 --- a/taiga/projects/notifications/models.py +++ b/taiga/projects/notifications/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/permissions.py b/taiga/projects/notifications/permissions.py index a89b9caf..699e0a4a 100644 --- a/taiga/projects/notifications/permissions.py +++ b/taiga/projects/notifications/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/serializers.py b/taiga/projects/notifications/serializers.py index 9b0b99cd..e0c988b5 100644 --- a/taiga/projects/notifications/serializers.py +++ b/taiga/projects/notifications/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index 6b805170..be6dc949 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/notifications/utils.py b/taiga/projects/notifications/utils.py index 510ab76a..3df08851 100644 --- a/taiga/projects/notifications/utils.py +++ b/taiga/projects/notifications/utils.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/notifications/validators.py b/taiga/projects/notifications/validators.py index b28e0712..a79cb8b7 100644 --- a/taiga/projects/notifications/validators.py +++ b/taiga/projects/notifications/validators.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/occ/__init__.py b/taiga/projects/occ/__init__.py index 34a54708..a25f77db 100644 --- a/taiga/projects/occ/__init__.py +++ b/taiga/projects/occ/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/occ/mixins.py b/taiga/projects/occ/mixins.py index 777a59b4..b473eb2b 100644 --- a/taiga/projects/occ/mixins.py +++ b/taiga/projects/occ/mixins.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index 140faddc..0925ce8a 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/api.py b/taiga/projects/references/api.py index 97b41de4..c515f6d1 100644 --- a/taiga/projects/references/api.py +++ b/taiga/projects/references/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/models.py b/taiga/projects/references/models.py index 583c69c8..6e141c8d 100644 --- a/taiga/projects/references/models.py +++ b/taiga/projects/references/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/permissions.py b/taiga/projects/references/permissions.py index 251e0f88..aa818c49 100644 --- a/taiga/projects/references/permissions.py +++ b/taiga/projects/references/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/sequences.py b/taiga/projects/references/sequences.py index 6c90abaa..ca6a4f62 100644 --- a/taiga/projects/references/sequences.py +++ b/taiga/projects/references/sequences.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/serializers.py b/taiga/projects/references/serializers.py index 6fb27432..ad9b5def 100644 --- a/taiga/projects/references/serializers.py +++ b/taiga/projects/references/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/references/services.py b/taiga/projects/references/services.py index c40ca311..203cd4e2 100644 --- a/taiga/projects/references/services.py +++ b/taiga/projects/references/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 5443c0c3..7a3380a8 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index 8a7284d9..d63cbe79 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/bulk_update_order.py b/taiga/projects/services/bulk_update_order.py index 40499346..83b38a90 100644 --- a/taiga/projects/services/bulk_update_order.py +++ b/taiga/projects/services/bulk_update_order.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/filters.py b/taiga/projects/services/filters.py index 5a2d1ff3..e9c3f28e 100644 --- a/taiga/projects/services/filters.py +++ b/taiga/projects/services/filters.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/modules_config.py b/taiga/projects/services/modules_config.py index 4b1cbae8..c0b92d77 100644 --- a/taiga/projects/services/modules_config.py +++ b/taiga/projects/services/modules_config.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index 88b2a47c..2a4753c4 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/services/tags_colors.py b/taiga/projects/services/tags_colors.py index 52ac61ff..14d1dbce 100644 --- a/taiga/projects/services/tags_colors.py +++ b/taiga/projects/services/tags_colors.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/signals.py b/taiga/projects/signals.py index 6cfb0ddf..b4652a8b 100644 --- a/taiga/projects/signals.py +++ b/taiga/projects/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/__init__.py b/taiga/projects/tasks/__init__.py index 0af24e1d..6e63190e 100644 --- a/taiga/projects/tasks/__init__.py +++ b/taiga/projects/tasks/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/admin.py b/taiga/projects/tasks/admin.py index 5295e7ca..4c451c17 100644 --- a/taiga/projects/tasks/admin.py +++ b/taiga/projects/tasks/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/api.py b/taiga/projects/tasks/api.py index 812458f1..d48791e2 100644 --- a/taiga/projects/tasks/api.py +++ b/taiga/projects/tasks/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/apps.py b/taiga/projects/tasks/apps.py index 8d9a112d..d495c964 100644 --- a/taiga/projects/tasks/apps.py +++ b/taiga/projects/tasks/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/models.py b/taiga/projects/tasks/models.py index 37176fab..c43869cb 100644 --- a/taiga/projects/tasks/models.py +++ b/taiga/projects/tasks/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/permissions.py b/taiga/projects/tasks/permissions.py index cf12a283..7a12cd13 100644 --- a/taiga/projects/tasks/permissions.py +++ b/taiga/projects/tasks/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/serializers.py b/taiga/projects/tasks/serializers.py index 81964367..deb9af68 100644 --- a/taiga/projects/tasks/serializers.py +++ b/taiga/projects/tasks/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/services.py b/taiga/projects/tasks/services.py index 8864d893..46cbedfd 100644 --- a/taiga/projects/tasks/services.py +++ b/taiga/projects/tasks/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/tasks/signals.py b/taiga/projects/tasks/signals.py index bdc622e9..07a7738d 100644 --- a/taiga/projects/tasks/signals.py +++ b/taiga/projects/tasks/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/translations.py b/taiga/projects/translations.py index 74b4b53e..b0b32671 100644 --- a/taiga/projects/translations.py +++ b/taiga/projects/translations.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/__init__.py b/taiga/projects/userstories/__init__.py index 572f9d9a..13d664b6 100644 --- a/taiga/projects/userstories/__init__.py +++ b/taiga/projects/userstories/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/admin.py b/taiga/projects/userstories/admin.py index 9cb3e667..2887e836 100644 --- a/taiga/projects/userstories/admin.py +++ b/taiga/projects/userstories/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 9fce8fb0..710e4461 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/apps.py b/taiga/projects/userstories/apps.py index 948e7c08..240e0375 100644 --- a/taiga/projects/userstories/apps.py +++ b/taiga/projects/userstories/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/models.py b/taiga/projects/userstories/models.py index 5424cb7f..2b29a275 100644 --- a/taiga/projects/userstories/models.py +++ b/taiga/projects/userstories/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/permissions.py b/taiga/projects/userstories/permissions.py index fb9361ab..95a8e622 100644 --- a/taiga/projects/userstories/permissions.py +++ b/taiga/projects/userstories/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/serializers.py b/taiga/projects/userstories/serializers.py index 35801ba0..c3f93d67 100644 --- a/taiga/projects/userstories/serializers.py +++ b/taiga/projects/userstories/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/services.py b/taiga/projects/userstories/services.py index c06309be..d1e54394 100644 --- a/taiga/projects/userstories/services.py +++ b/taiga/projects/userstories/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/signals.py b/taiga/projects/userstories/signals.py index 8764b60d..ce452992 100644 --- a/taiga/projects/userstories/signals.py +++ b/taiga/projects/userstories/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/userstories/validators.py b/taiga/projects/userstories/validators.py index 3efd7b8f..f8e2440d 100644 --- a/taiga/projects/userstories/validators.py +++ b/taiga/projects/userstories/validators.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/validators.py b/taiga/projects/validators.py index b6ed0509..11cd9f38 100644 --- a/taiga/projects/validators.py +++ b/taiga/projects/validators.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/votes/admin.py b/taiga/projects/votes/admin.py index 4e06ae8f..3ab238aa 100644 --- a/taiga/projects/votes/admin.py +++ b/taiga/projects/votes/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/votes/mixins/serializers.py b/taiga/projects/votes/mixins/serializers.py index c7f2eb5a..ed17d8a3 100644 --- a/taiga/projects/votes/mixins/serializers.py +++ b/taiga/projects/votes/mixins/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py index 8567b3d0..5148af7f 100644 --- a/taiga/projects/votes/mixins/viewsets.py +++ b/taiga/projects/votes/mixins/viewsets.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/votes/models.py b/taiga/projects/votes/models.py index a1af1ff7..6f5abbe5 100644 --- a/taiga/projects/votes/models.py +++ b/taiga/projects/votes/models.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/votes/serializers.py b/taiga/projects/votes/serializers.py index b6ab72a8..210c6057 100644 --- a/taiga/projects/votes/serializers.py +++ b/taiga/projects/votes/serializers.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/votes/services.py b/taiga/projects/votes/services.py index 8a6749dc..093b685e 100644 --- a/taiga/projects/votes/services.py +++ b/taiga/projects/votes/services.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/votes/utils.py b/taiga/projects/votes/utils.py index bc7d9d14..dd703bcf 100644 --- a/taiga/projects/votes/utils.py +++ b/taiga/projects/votes/utils.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/projects/wiki/admin.py b/taiga/projects/wiki/admin.py index ca929fb3..ed90f65c 100644 --- a/taiga/projects/wiki/admin.py +++ b/taiga/projects/wiki/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/wiki/api.py b/taiga/projects/wiki/api.py index dba3ccb0..60cfe8c0 100644 --- a/taiga/projects/wiki/api.py +++ b/taiga/projects/wiki/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/wiki/models.py b/taiga/projects/wiki/models.py index 4f8c6f4c..0cff1b21 100644 --- a/taiga/projects/wiki/models.py +++ b/taiga/projects/wiki/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/wiki/permissions.py b/taiga/projects/wiki/permissions.py index c64ac985..c1dd1e74 100644 --- a/taiga/projects/wiki/permissions.py +++ b/taiga/projects/wiki/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/projects/wiki/serializers.py b/taiga/projects/wiki/serializers.py index 22c9b8bd..43db324c 100644 --- a/taiga/projects/wiki/serializers.py +++ b/taiga/projects/wiki/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/routers.py b/taiga/routers.py index be507983..5e972b83 100644 --- a/taiga/routers.py +++ b/taiga/routers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/searches/api.py b/taiga/searches/api.py index 9fec9bb8..5a985664 100644 --- a/taiga/searches/api.py +++ b/taiga/searches/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/searches/serializers.py b/taiga/searches/serializers.py index ee713fff..3ec5f289 100644 --- a/taiga/searches/serializers.py +++ b/taiga/searches/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/searches/services.py b/taiga/searches/services.py index dcac7f33..d2f84798 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/stats/__init__.py b/taiga/stats/__init__.py index 92ed9d3d..28eceffa 100644 --- a/taiga/stats/__init__.py +++ b/taiga/stats/__init__.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/stats/api.py b/taiga/stats/api.py index bb2948e4..ab765ed9 100644 --- a/taiga/stats/api.py +++ b/taiga/stats/api.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/stats/apps.py b/taiga/stats/apps.py index e8b8e63a..dc893e5c 100644 --- a/taiga/stats/apps.py +++ b/taiga/stats/apps.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/stats/permissions.py b/taiga/stats/permissions.py index 0eb1a362..16fb72a8 100644 --- a/taiga/stats/permissions.py +++ b/taiga/stats/permissions.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/stats/routers.py b/taiga/stats/routers.py index 7257b473..dcab7819 100644 --- a/taiga/stats/routers.py +++ b/taiga/stats/routers.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/stats/services.py b/taiga/stats/services.py index 3e4fac83..61e30c51 100644 --- a/taiga/stats/services.py +++ b/taiga/stats/services.py @@ -1,4 +1,4 @@ -# Copyright (C) 2015 Taiga Agile LLC +# Copyright (C) 2014-2015 Taiga Agile LLC # 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 diff --git a/taiga/timeline/__init__.py b/taiga/timeline/__init__.py index 5e6960c6..21c6a821 100644 --- a/taiga/timeline/__init__.py +++ b/taiga/timeline/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/api.py b/taiga/timeline/api.py index 851a693f..49504499 100644 --- a/taiga/timeline/api.py +++ b/taiga/timeline/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/apps.py b/taiga/timeline/apps.py index cbdea045..7b926f9e 100644 --- a/taiga/timeline/apps.py +++ b/taiga/timeline/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py b/taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py index 7790b753..b4233923 100644 --- a/taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py +++ b/taiga/timeline/management/commands/_clear_unnecessary_new_membership_entries.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py b/taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py index 78e637d1..bac46080 100644 --- a/taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py +++ b/taiga/timeline/management/commands/_rebuild_timeline_for_user_creation.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py b/taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py index 1d145d30..ab1e4047 100644 --- a/taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py +++ b/taiga/timeline/management/commands/_update_timeline_for_updated_tasks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/management/commands/rebuild_timeline.py b/taiga/timeline/management/commands/rebuild_timeline.py index 5d95d4e4..a7a72955 100644 --- a/taiga/timeline/management/commands/rebuild_timeline.py +++ b/taiga/timeline/management/commands/rebuild_timeline.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py b/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py index 527a85bf..008da7b8 100644 --- a/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py +++ b/taiga/timeline/management/commands/rebuild_timeline_iterating_per_projects.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/models.py b/taiga/timeline/models.py index ac181842..c92a87c8 100644 --- a/taiga/timeline/models.py +++ b/taiga/timeline/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/permissions.py b/taiga/timeline/permissions.py index f07ee929..5aae5df4 100644 --- a/taiga/timeline/permissions.py +++ b/taiga/timeline/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/serializers.py b/taiga/timeline/serializers.py index 85a8c77f..f5defc56 100644 --- a/taiga/timeline/serializers.py +++ b/taiga/timeline/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/service.py b/taiga/timeline/service.py index a86965a1..cc03d676 100644 --- a/taiga/timeline/service.py +++ b/taiga/timeline/service.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/signals.py b/taiga/timeline/signals.py index 1a379f13..0aafb831 100644 --- a/taiga/timeline/signals.py +++ b/taiga/timeline/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/timeline/timeline_implementations.py b/taiga/timeline/timeline_implementations.py index 2b911607..8d85652c 100644 --- a/taiga/timeline/timeline_implementations.py +++ b/taiga/timeline/timeline_implementations.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/urls.py b/taiga/urls.py index 416da685..33407f0f 100644 --- a/taiga/urls.py +++ b/taiga/urls.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/admin.py b/taiga/users/admin.py index b2cd50cf..57055b2c 100644 --- a/taiga/users/admin.py +++ b/taiga/users/admin.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/api.py b/taiga/users/api.py index b635b1cd..4c44422c 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/filters.py b/taiga/users/filters.py index df7f04ae..8d2704b3 100644 --- a/taiga/users/filters.py +++ b/taiga/users/filters.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/forms.py b/taiga/users/forms.py index a5312c6a..7d9521a6 100644 --- a/taiga/users/forms.py +++ b/taiga/users/forms.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/gravatar.py b/taiga/users/gravatar.py index aaae895f..f26fde9d 100644 --- a/taiga/users/gravatar.py +++ b/taiga/users/gravatar.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/users/models.py b/taiga/users/models.py index 38b34559..b2828912 100644 --- a/taiga/users/models.py +++ b/taiga/users/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index d5600d88..5426d3f5 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index f5e5c6d8..24e5eb82 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/services.py b/taiga/users/services.py index d7f498d0..534b9b6e 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/signals.py b/taiga/users/signals.py index e61cec01..f1bac392 100644 --- a/taiga/users/signals.py +++ b/taiga/users/signals.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/users/validators.py b/taiga/users/validators.py index 6989e739..bc3a5a0c 100644 --- a/taiga/users/validators.py +++ b/taiga/users/validators.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/taiga/userstorage/api.py b/taiga/userstorage/api.py index 6755f3ac..b767e5d9 100644 --- a/taiga/userstorage/api.py +++ b/taiga/userstorage/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/userstorage/filters.py b/taiga/userstorage/filters.py index a46b1e61..31e1062c 100644 --- a/taiga/userstorage/filters.py +++ b/taiga/userstorage/filters.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/userstorage/models.py b/taiga/userstorage/models.py index 70dfb9ab..31be0aae 100644 --- a/taiga/userstorage/models.py +++ b/taiga/userstorage/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/userstorage/permissions.py b/taiga/userstorage/permissions.py index 24c1693d..e3a049cb 100644 --- a/taiga/userstorage/permissions.py +++ b/taiga/userstorage/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/userstorage/serializers.py b/taiga/userstorage/serializers.py index 92e01c3f..ecbac6a2 100644 --- a/taiga/userstorage/serializers.py +++ b/taiga/userstorage/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/__init__.py b/taiga/webhooks/__init__.py index 4f9173d3..57095ae0 100644 --- a/taiga/webhooks/__init__.py +++ b/taiga/webhooks/__init__.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/api.py b/taiga/webhooks/api.py index cda88dd7..8b254d10 100644 --- a/taiga/webhooks/api.py +++ b/taiga/webhooks/api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/apps.py b/taiga/webhooks/apps.py index f0444d68..589afa48 100644 --- a/taiga/webhooks/apps.py +++ b/taiga/webhooks/apps.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/models.py b/taiga/webhooks/models.py index 0704b77b..94e253aa 100644 --- a/taiga/webhooks/models.py +++ b/taiga/webhooks/models.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/permissions.py b/taiga/webhooks/permissions.py index 40463642..a2ef0207 100644 --- a/taiga/webhooks/permissions.py +++ b/taiga/webhooks/permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/serializers.py b/taiga/webhooks/serializers.py index 3b0c90c8..9ea5a120 100644 --- a/taiga/webhooks/serializers.py +++ b/taiga/webhooks/serializers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/signal_handlers.py b/taiga/webhooks/signal_handlers.py index 6d31e2bd..193b100b 100644 --- a/taiga/webhooks/signal_handlers.py +++ b/taiga/webhooks/signal_handlers.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/taiga/webhooks/tasks.py b/taiga/webhooks/tasks.py index d905167f..1933ed63 100644 --- a/taiga/webhooks/tasks.py +++ b/taiga/webhooks/tasks.py @@ -1,6 +1,6 @@ # Copyright (C) 2013 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/conftest.py b/tests/conftest.py index ed81ee80..fa3ae01f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/factories.py b/tests/factories.py index 2cb676b5..532faefe 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/fixtures.py b/tests/fixtures.py index 97cbea87..96d6a8fc 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py index e8e87048..4dcdfa7f 100644 --- a/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_issues_custom_attributes_resource.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py index 773c44cb..da70bd4e 100644 --- a/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_tasks_custom_attributes_resource.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py index 17a30bc8..766bae97 100644 --- a/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py +++ b/tests/integration/resources_permissions/test_userstories_custom_attributes_resource.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_auth_api.py b/tests/integration/test_auth_api.py index e776b569..e7a7805a 100644 --- a/tests/integration/test_auth_api.py +++ b/tests/integration/test_auth_api.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_custom_attributes_issues.py b/tests/integration/test_custom_attributes_issues.py index 65152aa5..a29c5d45 100644 --- a/tests/integration/test_custom_attributes_issues.py +++ b/tests/integration/test_custom_attributes_issues.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_custom_attributes_tasks.py b/tests/integration/test_custom_attributes_tasks.py index fee38830..6ffaf3b5 100644 --- a/tests/integration/test_custom_attributes_tasks.py +++ b/tests/integration/test_custom_attributes_tasks.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_custom_attributes_user_stories.py b/tests/integration/test_custom_attributes_user_stories.py index 6e602269..4b1f5079 100644 --- a/tests/integration/test_custom_attributes_user_stories.py +++ b/tests/integration/test_custom_attributes_user_stories.py @@ -1,6 +1,6 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_exporter_api.py b/tests/integration/test_exporter_api.py index dee2f5bb..69f58c32 100644 --- a/tests/integration/test_exporter_api.py +++ b/tests/integration/test_exporter_api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_history.py b/tests/integration/test_history.py index 4ea8efbd..1b5c4f48 100644 --- a/tests/integration/test_history.py +++ b/tests/integration/test_history.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_importer_api.py b/tests/integration/test_importer_api.py index d8fe6f50..8deb7975 100644 --- a/tests/integration/test_importer_api.py +++ b/tests/integration/test_importer_api.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_mdrender.py b/tests/integration/test_mdrender.py index 6710a014..cd075845 100644 --- a/tests/integration/test_mdrender.py +++ b/tests/integration/test_mdrender.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_milestones.py b/tests/integration/test_milestones.py index 932da487..d5675399 100644 --- a/tests/integration/test_milestones.py +++ b/tests/integration/test_milestones.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_neighbors.py b/tests/integration/test_neighbors.py index 33ee8d06..92a532dd 100644 --- a/tests/integration/test_neighbors.py +++ b/tests/integration/test_neighbors.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 7626769b..4fafc80b 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_occ.py b/tests/integration/test_occ.py index 2244750d..2cbdaced 100644 --- a/tests/integration/test_occ.py +++ b/tests/integration/test_occ.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_references_sequences.py b/tests/integration/test_references_sequences.py index f384791e..0ec9d35f 100644 --- a/tests/integration/test_references_sequences.py +++ b/tests/integration/test_references_sequences.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_roles.py b/tests/integration/test_roles.py index 70665aff..0bc773e7 100644 --- a/tests/integration/test_roles.py +++ b/tests/integration/test_roles.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_searches.py b/tests/integration/test_searches.py index db026a2a..c191682f 100644 --- a/tests/integration/test_searches.py +++ b/tests/integration/test_searches.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_throwttling.py b/tests/integration/test_throwttling.py index ce36d0c4..93c3a26d 100644 --- a/tests/integration/test_throwttling.py +++ b/tests/integration/test_throwttling.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/integration/test_timeline.py b/tests/integration/test_timeline.py index b526ac76..c96760e9 100644 --- a/tests/integration/test_timeline.py +++ b/tests/integration/test_timeline.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_us_autoclosing.py b/tests/integration/test_us_autoclosing.py index e2205bb0..a3b43c12 100644 --- a/tests/integration/test_us_autoclosing.py +++ b/tests/integration/test_us_autoclosing.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_userstorage_api.py b/tests/integration/test_userstorage_api.py index 1b642de2..d4050232 100644 --- a/tests/integration/test_userstorage_api.py +++ b/tests/integration/test_userstorage_api.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_vote_issues.py b/tests/integration/test_vote_issues.py index 5076b227..9b880d36 100644 --- a/tests/integration/test_vote_issues.py +++ b/tests/integration/test_vote_issues.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_vote_tasks.py b/tests/integration/test_vote_tasks.py index 7d71eff4..8b1a3605 100644 --- a/tests/integration/test_vote_tasks.py +++ b/tests/integration/test_vote_tasks.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_vote_userstories.py b/tests/integration/test_vote_userstories.py index 492f8121..ae118db1 100644 --- a/tests/integration/test_vote_userstories.py +++ b/tests/integration/test_vote_userstories.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_votes.py b/tests/integration/test_votes.py index cb7f3660..0408529e 100644 --- a/tests/integration/test_votes.py +++ b/tests/integration/test_votes.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_issues.py b/tests/integration/test_watch_issues.py index 91c64ec8..764bf6fd 100644 --- a/tests/integration/test_watch_issues.py +++ b/tests/integration/test_watch_issues.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_milestones.py b/tests/integration/test_watch_milestones.py index f744086c..acbbe85a 100644 --- a/tests/integration/test_watch_milestones.py +++ b/tests/integration/test_watch_milestones.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 8b1bf4da..724c5106 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_tasks.py b/tests/integration/test_watch_tasks.py index 449cba88..a23af44d 100644 --- a/tests/integration/test_watch_tasks.py +++ b/tests/integration/test_watch_tasks.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_userstories.py b/tests/integration/test_watch_userstories.py index 86ae3ef0..4b42ddb6 100644 --- a/tests/integration/test_watch_userstories.py +++ b/tests/integration/test_watch_userstories.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_watch_wikipages.py b/tests/integration/test_watch_wikipages.py index a26fe2a4..e90e2cd1 100644 --- a/tests/integration/test_watch_wikipages.py +++ b/tests/integration/test_watch_wikipages.py @@ -1,7 +1,7 @@ -# Copyright (C) 2015 Andrey Antukh -# Copyright (C) 2015 Jesús Espino -# Copyright (C) 2015 David Barragán -# Copyright (C) 2015 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/integration/test_webhooks.py b/tests/integration/test_webhooks.py index 9f4ecd71..4e33b919 100644 --- a/tests/integration/test_webhooks.py +++ b/tests/integration/test_webhooks.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/models.py b/tests/models.py index fe21e87c..87c667dc 100644 --- a/tests/models.py +++ b/tests/models.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/conftest.py b/tests/unit/conftest.py index e1159b0c..ac4f395d 100644 --- a/tests/unit/conftest.py +++ b/tests/unit/conftest.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/test_base_api_permissions.py b/tests/unit/test_base_api_permissions.py index dd736808..e0fc748c 100644 --- a/tests/unit/test_base_api_permissions.py +++ b/tests/unit/test_base_api_permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/unit/test_deferred.py b/tests/unit/test_deferred.py index c4e076c4..46afe862 100644 --- a/tests/unit/test_deferred.py +++ b/tests/unit/test_deferred.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/unit/test_export.py b/tests/unit/test_export.py index f95e44bd..17564fa7 100644 --- a/tests/unit/test_export.py +++ b/tests/unit/test_export.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/unit/test_gravatar.py b/tests/unit/test_gravatar.py index d91a90b2..ba1d18be 100644 --- a/tests/unit/test_gravatar.py +++ b/tests/unit/test_gravatar.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/unit/test_mdrender.py b/tests/unit/test_mdrender.py index d0404c13..a58e5cca 100644 --- a/tests/unit/test_mdrender.py +++ b/tests/unit/test_mdrender.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/test_permissions.py b/tests/unit/test_permissions.py index c1a0e765..b4c0ae45 100644 --- a/tests/unit/test_permissions.py +++ b/tests/unit/test_permissions.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/unit/test_slug.py b/tests/unit/test_slug.py index e687bcbd..65fb4cf0 100644 --- a/tests/unit/test_slug.py +++ b/tests/unit/test_slug.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/test_timeline.py b/tests/unit/test_timeline.py index 377e9728..7e8ff24b 100644 --- a/tests/unit/test_timeline.py +++ b/tests/unit/test_timeline.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/test_tokens.py b/tests/unit/test_tokens.py index 1da085e1..ca2b09b2 100644 --- a/tests/unit/test_tokens.py +++ b/tests/unit/test_tokens.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 0bb5922a..9521974b 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -1,6 +1,6 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 diff --git a/tests/utils.py b/tests/utils.py index 8245e2dc..389d9c59 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,7 +1,7 @@ -# Copyright (C) 2014 Andrey Antukh -# Copyright (C) 2014 Jesús Espino -# Copyright (C) 2014 David Barragán -# Copyright (C) 2014 Anler Hernández +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the From 7ec825374943cb495bf378ad5f893a74ad46d5dd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 14 Oct 2015 14:08:40 +0200 Subject: [PATCH 162/190] Fixing is_watcher attribute on TIU patch requests --- taiga/projects/notifications/mixins.py | 7 +++++- tests/integration/test_watch_issues.py | 25 +++++++++++++++++++++ tests/integration/test_watch_tasks.py | 23 +++++++++++++++++++ tests/integration/test_watch_userstories.py | 22 ++++++++++++++++++ 4 files changed, 76 insertions(+), 1 deletion(-) diff --git a/taiga/projects/notifications/mixins.py b/taiga/projects/notifications/mixins.py index f62d1157..a147431b 100644 --- a/taiga/projects/notifications/mixins.py +++ b/taiga/projects/notifications/mixins.py @@ -228,10 +228,15 @@ class EditableWatchedResourceModelSerializer(WatchedResourceModelSerializer): return obj def to_native(self, obj): - #watchers is wasn't attached via the get_queryset of the viewset we need to manually add it + #if watchers wasn't attached via the get_queryset of the viewset we need to manually add it if obj is not None and not hasattr(obj, "watchers"): obj.watchers = [user.id for user in obj.get_watchers()] + request = self.context.get("request", None) + user = request.user if request else None + if user and user.is_authenticated(): + obj.is_watcher = user.id in obj.watchers + return super(WatchedResourceModelSerializer, self).to_native(obj) def save(self, **kwargs): diff --git a/tests/integration/test_watch_issues.py b/tests/integration/test_watch_issues.py index 764bf6fd..36ac157e 100644 --- a/tests/integration/test_watch_issues.py +++ b/tests/integration/test_watch_issues.py @@ -122,3 +122,28 @@ def test_get_issue_is_watcher(client): assert response.status_code == 200 assert response.data['watchers'] == [] assert response.data['is_watcher'] == False + + +def test_remove_issue_watcher(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + issue = f.IssueFactory(project=project, + status__project=project, + severity__project=project, + priority__project=project, + type__project=project, + milestone__project=project) + + issue.add_watcher(user) + role = f.RoleFactory.create(project=project, permissions=['modify_issue', 'view_issues']) + f.MembershipFactory.create(project=project, user=user, role=role) + + url = reverse("issues-detail", args=(issue.id,)) + + client.login(user) + + data = {"version": issue.version, "watchers": []} + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_tasks.py b/tests/integration/test_watch_tasks.py index a23af44d..03b70190 100644 --- a/tests/integration/test_watch_tasks.py +++ b/tests/integration/test_watch_tasks.py @@ -122,3 +122,26 @@ def test_get_task_is_watcher(client): assert response.status_code == 200 assert response.data['watchers'] == [] assert response.data['is_watcher'] == False + + +def test_remove_task_watcher(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + task = f.TaskFactory(project=project, + user_story=None, + status__project=project, + milestone__project=project) + + task.add_watcher(user) + role = f.RoleFactory.create(project=project, permissions=['modify_task', 'view_tasks']) + f.MembershipFactory.create(project=project, user=user, role=role) + + url = reverse("tasks-detail", args=(task.id,)) + + client.login(user) + + data = {"version": task.version, "watchers": []} + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watcher'] == False diff --git a/tests/integration/test_watch_userstories.py b/tests/integration/test_watch_userstories.py index 4b42ddb6..aaf797b8 100644 --- a/tests/integration/test_watch_userstories.py +++ b/tests/integration/test_watch_userstories.py @@ -122,3 +122,25 @@ def test_get_user_story_is_watcher(client): assert response.status_code == 200 assert response.data['watchers'] == [] assert response.data['is_watcher'] == False + + +def test_remove_user_story_watcher(client): + user = f.UserFactory.create() + project = f.ProjectFactory.create() + us = f.UserStoryFactory(project=project, + status__project=project, + milestone__project=project) + + us.add_watcher(user) + role = f.RoleFactory.create(project=project, permissions=['modify_us', 'view_us']) + f.MembershipFactory.create(project=project, user=user, role=role) + + url = reverse("userstories-detail", args=(us.id,)) + + client.login(user) + + data = {"version": us.version, "watchers": []} + response = client.json.patch(url, json.dumps(data)) + assert response.status_code == 200 + assert response.data['watchers'] == [] + assert response.data['is_watcher'] == False From 67636f1b3218e77592614b1a61b7b012dcee5f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 13 Oct 2015 15:13:36 +0200 Subject: [PATCH 163/190] Fix issue #3321: Emoji extension uses wrong path --- settings/common.py | 3 --- taiga/mdrender/extensions/emojify.py | 13 +++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/settings/common.py b/settings/common.py index 544758d3..7e0cfd3c 100644 --- a/settings/common.py +++ b/settings/common.py @@ -193,9 +193,6 @@ MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage" # urls depends on it. On production should be set # something like https://media.taiga.io/ MEDIA_URL = "http://localhost:8000/media/" - -# Static url is not widelly used by taiga (only -# if admin is activated). STATIC_URL = "http://localhost:8000/static/" # Static configuration. diff --git a/taiga/mdrender/extensions/emojify.py b/taiga/mdrender/extensions/emojify.py index cdf986ef..020ba329 100644 --- a/taiga/mdrender/extensions/emojify.py +++ b/taiga/mdrender/extensions/emojify.py @@ -27,7 +27,8 @@ import re -from django.conf import settings +from django.templatetags.static import static + from markdown.extensions import Extension from markdown.preprocessors import Preprocessor @@ -35,8 +36,8 @@ from markdown.preprocessors import Preprocessor # Grab the emojis (+800) here: https://github.com/arvida/emoji-cheat-sheet.com # This **crazy long** list was generated by walking through the emojis.png -emojis_path = "{}://{}/static/img/emojis/".format(settings.SITES["api"]["scheme"], settings.SITES["api"]["domain"]) -emojis_set = { +EMOJIS_PATH = "img/emojis/" +EMOJIS_SET = { "+1", "-1", "100", "1234", "8ball", "a", "ab", "abc", "abcd", "accept", "aerial_tramway", "airplane", "alarm_clock", "alien", "ambulance", "anchor", "angel", "anger", "angry", "anguished", "ant", "apple", "aquarius", "aries", "arrows_clockwise", "arrows_counterclockwise", "arrow_backward", "arrow_double_down", @@ -168,11 +169,11 @@ class EmojifyPreprocessor(Preprocessor): def emojify(match): emoji = match.group(1) - if emoji not in emojis_set: + if emoji not in EMOJIS_SET: return match.group(0) - url = emojis_path + emoji + u'.png' - + path = "{}{}.png".format(EMOJIS_PATH, emoji) + url = static(path) return '![{emoji}]({url})'.format(emoji=emoji, url=url) for line in lines: From 42da17212ccb114b444b835ae142426828e54a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 14 Oct 2015 18:49:30 +0200 Subject: [PATCH 164/190] Fix custom attributes --- CHANGELOG.md | 2 +- taiga/projects/custom_attributes/choices.py | 28 ++++++++++++++++ .../0006_add_customattribute_field_type.py | 32 ------------------- .../migrations/0006_auto_20151014_1645.py | 29 +++++++++++++++++ taiga/projects/custom_attributes/models.py | 10 +++--- 5 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 taiga/projects/custom_attributes/choices.py delete mode 100644 taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py create mode 100644 taiga/projects/custom_attributes/migrations/0006_auto_20151014_1645.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 465ef394..b134e802 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ## 1.9.0 ??? (unreleased) ### Features -- Add a "field type" property for custom fields: 'text' and 'multiline text' right now (thanks to [@artlepool](https://github.com/artlepool)). +- Add a "field type" property for custom fields: 'text', 'multiline text' and 'date' right now (thanks to [@artlepool](https://github.com/artlepool)). - Allow multiple actions in the commit messages. - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). - Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. diff --git a/taiga/projects/custom_attributes/choices.py b/taiga/projects/custom_attributes/choices.py new file mode 100644 index 00000000..9c3e8468 --- /dev/null +++ b/taiga/projects/custom_attributes/choices.py @@ -0,0 +1,28 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 django.utils.translation import ugettext_lazy as _ + + +TEXT_TYPE = "text" +MULTILINE_TYPE = "multiline" +DATE_TYPE = "date" + +TYPES_CHOICES = ( + (TEXT_TYPE, _("Text")), + (MULTILINE_TYPE, _("Multi-Line Text")), + (DATE_TYPE, _("Date")) +) diff --git a/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py b/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py deleted file mode 100644 index 8a4aebb8..00000000 --- a/taiga/projects/custom_attributes/migrations/0006_add_customattribute_field_type.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('custom_attributes', '0005_auto_20150505_1639'), - ] - - operations = [ - migrations.AddField( - model_name='issuecustomattribute', - name='field_type', - field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), - preserve_default=True, - ), - migrations.AddField( - model_name='taskcustomattribute', - name='field_type', - field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), - preserve_default=True, - ), - migrations.AddField( - model_name='userstorycustomattribute', - name='field_type', - field=models.CharField(max_length=5, verbose_name='type', choices=[('TEXT', 'Text'), ('MULTI', 'Multi-Line Text')], default='TEXT'), - preserve_default=True, - ), - ] diff --git a/taiga/projects/custom_attributes/migrations/0006_auto_20151014_1645.py b/taiga/projects/custom_attributes/migrations/0006_auto_20151014_1645.py new file mode 100644 index 00000000..85698a38 --- /dev/null +++ b/taiga/projects/custom_attributes/migrations/0006_auto_20151014_1645.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('custom_attributes', '0005_auto_20150505_1639'), + ] + + operations = [ + migrations.AddField( + model_name='issuecustomattribute', + name='type', + field=models.CharField(default='text', choices=[('text', 'Text'), ('multiline', 'Multi-Line Text'), ('date', 'Date')], verbose_name='type', max_length=16), + ), + migrations.AddField( + model_name='taskcustomattribute', + name='type', + field=models.CharField(default='text', choices=[('text', 'Text'), ('multiline', 'Multi-Line Text'), ('date', 'Date')], verbose_name='type', max_length=16), + ), + migrations.AddField( + model_name='userstorycustomattribute', + name='type', + field=models.CharField(default='text', choices=[('text', 'Text'), ('multiline', 'Multi-Line Text'), ('date', 'Date')], verbose_name='type', max_length=16), + ), + ] diff --git a/taiga/projects/custom_attributes/models.py b/taiga/projects/custom_attributes/models.py index e75a34e1..51c81db6 100644 --- a/taiga/projects/custom_attributes/models.py +++ b/taiga/projects/custom_attributes/models.py @@ -22,6 +22,8 @@ from django_pgjson.fields import JsonField from taiga.projects.occ.mixins import OCCModelMixin +from . import choices + ###################################################### # Custom Attribute Models @@ -29,13 +31,11 @@ from taiga.projects.occ.mixins import OCCModelMixin class AbstractCustomAttribute(models.Model): - FIELD_TYPES = ( - ("TEXT", _("Text")), - ("MULTI", _("Multi-Line Text")) - ) name = models.CharField(null=False, blank=False, max_length=64, verbose_name=_("name")) description = models.TextField(null=False, blank=True, verbose_name=_("description")) - field_type = models.CharField(null=False, blank=False, choices=FIELD_TYPES, max_length=5, default="TEXT", verbose_name=_("type")) + type = models.CharField(null=False, blank=False, max_length=16, + choices=choices.TYPES_CHOICES, default=choices.TEXT_TYPE, + verbose_name=_("type")) order = models.IntegerField(null=False, blank=False, default=10000, verbose_name=_("order")) project = models.ForeignKey("projects.Project", null=False, blank=False, related_name="%(class)ss", verbose_name=_("project")) From 99b3477864c11187d58e6343d68ed2307f4ecf8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 15 Oct 2015 10:12:25 +0200 Subject: [PATCH 165/190] Generate custom attributes with random types in sample data --- .../management/commands/sample_data.py | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 84a941e1..55605c75 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -35,6 +35,7 @@ from taiga.projects.issues.models import * from taiga.projects.wiki.models import * from taiga.projects.attachments.models import * from taiga.projects.custom_attributes.models import * +from taiga.projects.custom_attributes.choices import TYPES_CHOICES, TEXT_TYPE, MULTILINE_TYPE, DATE_TYPE from taiga.projects.history.services import take_snapshot from taiga.projects.votes.services import add_vote from taiga.events.apps import disconnect_events_signals @@ -157,18 +158,21 @@ class Command(BaseCommand): for i in range(1, 4): UserStoryCustomAttribute.objects.create(name=self.sd.words(1, 3), description=self.sd.words(3, 12), + type=self.sd.choice(TYPES_CHOICES)[0], project=project, order=i) if self.sd.boolean: for i in range(1, 4): TaskCustomAttribute.objects.create(name=self.sd.words(1, 3), description=self.sd.words(3, 12), + type=self.sd.choice(TYPES_CHOICES)[0], project=project, order=i) if self.sd.boolean: for i in range(1, 4): IssueCustomAttribute.objects.create(name=self.sd.words(1, 3), description=self.sd.words(3, 12), + type=self.sd.choice(TYPES_CHOICES)[0], project=project, order=i) @@ -256,6 +260,15 @@ class Command(BaseCommand): return wiki_page + def get_custom_attributes_value(self, type): + if type == TEXT_TYPE: + return self.sd.words(1, 12) + if type == MULTILINE_TYPE: + return self.sd.paragraphs(2, 4) + if type == DATE_TYPE: + return self.sd.future_date(min_distance=0, max_distance=365) + return None + def create_bug(self, project): bug = Issue.objects.create(project=project, subject=self.sd.choice(SUBJECT_CHOICES), @@ -274,8 +287,8 @@ class Command(BaseCommand): bug.save() - custom_attributes_values = {str(ca.id): self.sd.words(1, 12) for ca in project.issuecustomattributes.all() - if self.sd.boolean()} + custom_attributes_values = {str(ca.id): self.get_custom_attributes_value(ca.type) for ca + in project.issuecustomattributes.all() if self.sd.boolean()} if custom_attributes_values: bug.custom_attributes_values.attributes_values = custom_attributes_values bug.custom_attributes_values.save() @@ -327,8 +340,8 @@ class Command(BaseCommand): task.save() - custom_attributes_values = {str(ca.id): self.sd.words(1, 12) for ca in project.taskcustomattributes.all() - if self.sd.boolean()} + custom_attributes_values = {str(ca.id): self.get_custom_attributes_value(ca.type) for ca + in project.taskcustomattributes.all() if self.sd.boolean()} if custom_attributes_values: task.custom_attributes_values.attributes_values = custom_attributes_values task.custom_attributes_values.save() @@ -376,8 +389,8 @@ class Command(BaseCommand): us.save() - custom_attributes_values = {str(ca.id): self.sd.words(1, 12) for ca in project.userstorycustomattributes.all() - if self.sd.boolean()} + custom_attributes_values = {str(ca.id): self.get_custom_attributes_value(ca.type) for ca + in project.userstorycustomattributes.all() if self.sd.boolean()} if custom_attributes_values: us.custom_attributes_values.attributes_values = custom_attributes_values us.custom_attributes_values.save() @@ -387,7 +400,8 @@ class Command(BaseCommand): attachment = self.create_attachment(us, i+1) if self.sd.choice([True, True, False, True, True]): - us.assigned_to = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user + us.assigned_to = self.sd.db_object_from_queryset(project.memberships.filter( + user__isnull=False)).user us.save() watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user From d6941d661273973b04c20942b9b411f2ea9486aa Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 15 Oct 2015 07:47:54 +0200 Subject: [PATCH 166/190] Issue 2731: Using utf-8 chars in wiki titles --- taiga/projects/wiki/models.py | 7 +++++++ taiga/projects/wiki/serializers.py | 1 + 2 files changed, 8 insertions(+) diff --git a/taiga/projects/wiki/models.py b/taiga/projects/wiki/models.py index 0cff1b21..de3cb25c 100644 --- a/taiga/projects/wiki/models.py +++ b/taiga/projects/wiki/models.py @@ -19,6 +19,7 @@ from django.contrib.contenttypes import generic from django.conf import settings from django.utils.translation import ugettext_lazy as _ from django.utils import timezone +from taiga.base.utils.slug import slugify from taiga.projects.notifications.mixins import WatchedModelMixin from taiga.projects.occ import OCCModelMixin @@ -78,3 +79,9 @@ class WikiLink(models.Model): def __str__(self): return self.title + + def save(self, *args, **kwargs): + if not self.href: + self.href = slugify(self.title) + + super().save(*args, **kwargs) diff --git a/taiga/projects/wiki/serializers.py b/taiga/projects/wiki/serializers.py index 43db324c..d1dfc938 100644 --- a/taiga/projects/wiki/serializers.py +++ b/taiga/projects/wiki/serializers.py @@ -41,3 +41,4 @@ class WikiPageSerializer(WatchersValidator, WatchedResourceModelSerializer, seri class WikiLinkSerializer(serializers.ModelSerializer): class Meta: model = models.WikiLink + read_only_fields = ('href',) From 09bbeb25fa61c8dbd46b0a6811eaffc591d07f58 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 15 Oct 2015 14:52:51 +0200 Subject: [PATCH 167/190] Add template config for Django templates --- settings/common.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/settings/common.py b/settings/common.py index 7e0cfd3c..9568ad8c 100644 --- a/settings/common.py +++ b/settings/common.py @@ -234,6 +234,24 @@ TEMPLATES = [ "match_extension": ".jinja", } }, + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(BASE_DIR, "templates"), + ], + "APP_DIRS": True, + "OPTIONS": { + 'context_processors': [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.request", + "django.template.context_processors.i18n", + "django.template.context_processors.media", + "django.template.context_processors.static", + "django.template.context_processors.tz", + "django.contrib.messages.context_processors.messages", + ], + } + }, ] From 9486387cc2a432ecbc976b389f3b5f77ae35afd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 20 Oct 2015 16:08:16 +0200 Subject: [PATCH 168/190] Update CHANGELOG --- CHANGELOG.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b134e802..f2c92439 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,21 +4,20 @@ ## 1.9.0 ??? (unreleased) ### Features + +- Project can be starred or unstarred and the fans list can be obtained. +- US, tasks and Issues can be upvoted or downvoted and the voters list can be obtained. +- Now users can watch public issues, tasks and user stories. +- Add endpoints to show the watchers list for issues, tasks and user stories. - Add a "field type" property for custom fields: 'text', 'multiline text' and 'date' right now (thanks to [@artlepool](https://github.com/artlepool)). - Allow multiple actions in the commit messages. - Now every user that coments USs, Issues or Tasks will be involved in it (add author to the watchers list). -- Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. -- Add custom videoconference system. -- Add support for comments in the Gitlab webhooks integration. - Now profile timelines only show content about the objects (US/Tasks/Issues/Wiki pages) you are involved. -- US, tasks and Issues can be upvoted or downvoted and the voters list can be obtained. -- Project can be starred or unstarred and the fans list can be obtained. -- Now users can watch public issues, tasks and user stories. -- Add endpoints to show the watchers list for issues, tasks and user stories. -- Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). +- Add custom videoconference system. +- Fix the compatibility with BitBucket webhooks and add issues and issues comments integration. +- Add support for comments in the Gitlab webhooks integration. - Add externall apps: now Taiga can integrate with hundreds of applications and service. - Improve searching system, now full text searchs are supported -- Improve export system, now is more efficient and prevents possible crashes with heavy projects. - Add sha1 hash to attachments to verify the integrity of files (thanks to [@astagi](https://github.com/astagi)). - i18n. - Add italian (it) translation. @@ -26,15 +25,16 @@ - Add portuguese (Brazil) (pt_BR) translation. - Add russian (ru) translation. - ### Misc - Made compatible with python 3.5. - Migrated to django 1.8. - Update the rest of requirements to the last version. +- Improve export system, now is more efficient and prevents possible crashes with heavy projects. - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) - API: Improve and fix some errors in issues/filters_data and userstories/filters_data. - Webhooks: Add deleted datetime to webhooks responses when isues, tasks or USs are deleted. +- Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). - Lots of small and not so small bugfixes. From 3fc725c080e77044a3da1116e21212b98db6c637 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 19 Oct 2015 14:18:01 +0200 Subject: [PATCH 169/190] Refactoring likes and votes --- settings/common.py | 1 + taiga/projects/api.py | 4 +- taiga/projects/likes/__init__.py | 0 taiga/projects/likes/admin.py | 25 ++ .../projects/likes/migrations/0001_initial.py | 51 ++++ taiga/projects/likes/migrations/__init__.py | 0 taiga/projects/likes/mixins/__init__.py | 0 taiga/projects/likes/mixins/serializers.py | 30 ++ taiga/projects/likes/mixins/viewsets.py | 92 ++++++ taiga/projects/likes/models.py | 66 +++++ taiga/projects/likes/serializers.py | 30 ++ taiga/projects/likes/services.py | 114 ++++++++ taiga/projects/likes/utils.py | 76 +++++ .../management/commands/sample_data.py | 50 ++-- taiga/projects/serializers.py | 2 +- taiga/projects/services/stats.py | 4 +- taiga/projects/votes/mixins/serializers.py | 15 +- taiga/projects/votes/mixins/viewsets.py | 46 +-- taiga/users/api.py | 266 ++++++++++-------- taiga/users/permissions.py | 1 + taiga/users/serializers.py | 39 ++- taiga/users/services.py | 233 +++++++++++++-- tests/factories.py | 21 +- .../test_projects_resource.py | 18 +- .../test_users_resources.py | 12 + tests/integration/test_fan_projects.py | 123 ++++++++ tests/integration/test_users.py | 211 +++++++++++--- 27 files changed, 1231 insertions(+), 299 deletions(-) create mode 100644 taiga/projects/likes/__init__.py create mode 100644 taiga/projects/likes/admin.py create mode 100644 taiga/projects/likes/migrations/0001_initial.py create mode 100644 taiga/projects/likes/migrations/__init__.py create mode 100644 taiga/projects/likes/mixins/__init__.py create mode 100644 taiga/projects/likes/mixins/serializers.py create mode 100644 taiga/projects/likes/mixins/viewsets.py create mode 100644 taiga/projects/likes/models.py create mode 100644 taiga/projects/likes/serializers.py create mode 100644 taiga/projects/likes/services.py create mode 100644 taiga/projects/likes/utils.py create mode 100644 tests/integration/test_fan_projects.py diff --git a/settings/common.py b/settings/common.py index 9568ad8c..355a6597 100644 --- a/settings/common.py +++ b/settings/common.py @@ -295,6 +295,7 @@ INSTALLED_APPS = [ "taiga.projects.history", "taiga.projects.notifications", "taiga.projects.attachments", + "taiga.projects.likes", "taiga.projects.votes", "taiga.projects.milestones", "taiga.projects.userstories", diff --git a/taiga/projects/api.py b/taiga/projects/api.py index efec71c1..4b3c47aa 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -44,7 +44,7 @@ from taiga.projects.mixins.on_destroy import MoveOnDestroyMixin from taiga.projects.userstories.models import UserStory, RolePoints from taiga.projects.tasks.models import Task from taiga.projects.issues.models import Issue -from taiga.projects.votes.mixins.viewsets import LikedResourceMixin, FansViewSetMixin +from taiga.projects.likes.mixins.viewsets import LikedResourceMixin, FansViewSetMixin from taiga.permissions import service as permissions_service from . import serializers @@ -68,7 +68,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, ModelCrudViewSet) def get_queryset(self): qs = super().get_queryset() - qs = self.attach_votes_attrs_to_queryset(qs) + qs = self.attach_likes_attrs_to_queryset(qs) qs = attach_project_total_watchers_attrs_to_queryset(qs) if self.request.user.is_authenticated(): qs = attach_project_is_watcher_to_queryset(qs, self.request.user) diff --git a/taiga/projects/likes/__init__.py b/taiga/projects/likes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/likes/admin.py b/taiga/projects/likes/admin.py new file mode 100644 index 00000000..802eaca4 --- /dev/null +++ b/taiga/projects/likes/admin.py @@ -0,0 +1,25 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 django.contrib import admin +from django.contrib.contenttypes.admin import GenericTabularInline + +from . import models + + +class LikeInline(GenericTabularInline): + model = models.Like + extra = 0 diff --git a/taiga/projects/likes/migrations/0001_initial.py b/taiga/projects/likes/migrations/0001_initial.py new file mode 100644 index 00000000..e1a9dd6d --- /dev/null +++ b/taiga/projects/likes/migrations/0001_initial.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +from django.conf import settings + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Like', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('created_date', models.DateTimeField(verbose_name='created date', auto_now_add=True)), + ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ('user', models.ForeignKey(related_name='likes', verbose_name='user', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'Like', + 'verbose_name_plural': 'Likes', + }, + ), + migrations.CreateModel( + name='Likes', + fields=[ + ('id', models.AutoField(serialize=False, primary_key=True, auto_created=True, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('count', models.PositiveIntegerField(default=0, verbose_name='count')), + ('content_type', models.ForeignKey(to='contenttypes.ContentType')), + ], + options={ + 'verbose_name': 'Likes', + 'verbose_name_plural': 'Likes', + }, + ), + migrations.AlterUniqueTogether( + name='likes', + unique_together=set([('content_type', 'object_id')]), + ), + migrations.AlterUniqueTogether( + name='like', + unique_together=set([('content_type', 'object_id', 'user')]), + ), + ] diff --git a/taiga/projects/likes/migrations/__init__.py b/taiga/projects/likes/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/likes/mixins/__init__.py b/taiga/projects/likes/mixins/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/taiga/projects/likes/mixins/serializers.py b/taiga/projects/likes/mixins/serializers.py new file mode 100644 index 00000000..a4875b86 --- /dev/null +++ b/taiga/projects/likes/mixins/serializers.py @@ -0,0 +1,30 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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.base.api import serializers + + +class FanResourceSerializerMixin(serializers.ModelSerializer): + is_fan = serializers.SerializerMethodField("get_is_fan") + total_fans = serializers.SerializerMethodField("get_total_fans") + + def get_is_fan(self, obj): + # The "is_fan" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "is_fan", False) or False + + def get_total_fans(self, obj): + # The "total_fans" attribute is attached in the get_queryset of the viewset. + return getattr(obj, "total_fans", 0) or 0 diff --git a/taiga/projects/likes/mixins/viewsets.py b/taiga/projects/likes/mixins/viewsets.py new file mode 100644 index 00000000..b3d9b2e1 --- /dev/null +++ b/taiga/projects/likes/mixins/viewsets.py @@ -0,0 +1,92 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 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 django.core.exceptions import ObjectDoesNotExist + +from taiga.base import response +from taiga.base.api import viewsets +from taiga.base.api.utils import get_object_or_404 +from taiga.base.decorators import detail_route + +from taiga.projects.likes import serializers +from taiga.projects.likes import services +from taiga.projects.likes.utils import attach_total_fans_to_queryset, attach_is_fan_to_queryset + + +class LikedResourceMixin: + # Note: Update get_queryset method: + # def get_queryset(self): + # qs = super().get_queryset() + # return self.attach_likes_attrs_to_queryset(qs) + + def attach_likes_attrs_to_queryset(self, queryset): + qs = attach_total_fans_to_queryset(queryset) + + if self.request.user.is_authenticated(): + qs = attach_is_fan_to_queryset(self.request.user, qs) + + return qs + + @detail_route(methods=["POST"]) + def like(self, request, pk=None): + obj = self.get_object() + self.check_permissions(request, "like", obj) + + services.add_like(obj, user=request.user) + return response.Ok() + + @detail_route(methods=["POST"]) + def unlike(self, request, pk=None): + obj = self.get_object() + self.check_permissions(request, "unlike", obj) + + services.remove_like(obj, user=request.user) + return response.Ok() + + +class FansViewSetMixin: + # Is a ModelListViewSet with two required params: permission_classes and resource_model + serializer_class = serializers.FanSerializer + list_serializer_class = serializers.FanSerializer + permission_classes = None + resource_model = None + + def retrieve(self, request, *args, **kwargs): + pk = kwargs.get("pk", None) + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'retrieve', resource) + + try: + self.object = services.get_fans(resource).get(pk=pk) + except ObjectDoesNotExist: # or User.DoesNotExist + return response.NotFound() + + serializer = self.get_serializer(self.object) + return response.Ok(serializer.data) + + def list(self, request, *args, **kwargs): + resource_id = kwargs.get("resource_id", None) + resource = get_object_or_404(self.resource_model, pk=resource_id) + + self.check_permissions(request, 'list', resource) + + return super().list(request, *args, **kwargs) + + def get_queryset(self): + resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) + return services.get_fans(resource) diff --git a/taiga/projects/likes/models.py b/taiga/projects/likes/models.py new file mode 100644 index 00000000..9b56f923 --- /dev/null +++ b/taiga/projects/likes/models.py @@ -0,0 +1,66 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.conf import settings +from django.contrib.contenttypes import generic +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class Likes(models.Model): + content_type = models.ForeignKey("contenttypes.ContentType") + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey("content_type", "object_id") + count = models.PositiveIntegerField(null=False, blank=False, default=0, verbose_name=_("count")) + + class Meta: + verbose_name = _("Likes") + verbose_name_plural = _("Likes") + unique_together = ("content_type", "object_id") + + @property + def project(self): + if hasattr(self.content_object, 'project'): + return self.content_object.project + return None + + def __str__(self): + return self.count + + +class Like(models.Model): + content_type = models.ForeignKey("contenttypes.ContentType") + object_id = models.PositiveIntegerField() + content_object = generic.GenericForeignKey("content_type", "object_id") + user = models.ForeignKey(settings.AUTH_USER_MODEL, null=False, blank=False, + related_name="likes", verbose_name=_("user")) + created_date = models.DateTimeField(null=False, blank=False, auto_now_add=True, + verbose_name=_("created date")) + + class Meta: + verbose_name = _("Like") + verbose_name_plural = _("Likes") + unique_together = ("content_type", "object_id", "user") + + @property + def project(self): + if hasattr(self.content_object, 'project'): + return self.content_object.project + return None + + def __str__(self): + return self.user.get_full_name() diff --git a/taiga/projects/likes/serializers.py b/taiga/projects/likes/serializers.py new file mode 100644 index 00000000..c507166e --- /dev/null +++ b/taiga/projects/likes/serializers.py @@ -0,0 +1,30 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from taiga.base.api import serializers +from taiga.base.fields import TagsField + +from taiga.users.models import User +from taiga.users.services import get_photo_or_gravatar_url + + +class FanSerializer(serializers.ModelSerializer): + full_name = serializers.CharField(source='get_full_name', required=False) + + class Meta: + model = User + fields = ('id', 'username', 'full_name') diff --git a/taiga/projects/likes/services.py b/taiga/projects/likes/services.py new file mode 100644 index 00000000..f9b94a7a --- /dev/null +++ b/taiga/projects/likes/services.py @@ -0,0 +1,114 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.db.models import F +from django.db.transaction import atomic +from django.apps import apps +from django.contrib.auth import get_user_model + +from .models import Likes, Like + + +def add_like(obj, user): + """Add a like to an object. + + If the user has already liked the object nothing happends, so this function can be considered + idempotent. + + :param obj: Any Django model instance. + :param user: User adding the like. :class:`~taiga.users.models.User` instance. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + with atomic(): + like, created = Like.objects.get_or_create(content_type=obj_type, object_id=obj.id, user=user) + if not created: + return + + likes, _ = Likes.objects.get_or_create(content_type=obj_type, object_id=obj.id) + likes.count = F('count') + 1 + likes.save() + return like + + +def remove_like(obj, user): + """Remove an user like from an object. + + If the user has not liked the object nothing happens so this function can be considered + idempotent. + + :param obj: Any Django model instance. + :param user: User removing her like. :class:`~taiga.users.models.User` instance. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + with atomic(): + qs = Like.objects.filter(content_type=obj_type, object_id=obj.id, user=user) + if not qs.exists(): + return + + qs.delete() + + likes, _ = Likes.objects.get_or_create(content_type=obj_type, object_id=obj.id) + likes.count = F('count') - 1 + likes.save() + + +def get_fans(obj): + """Get the fans of an object. + + :param obj: Any Django model instance. + + :return: User queryset object representing the users that liked the object. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + return get_user_model().objects.filter(likes__content_type=obj_type, likes__object_id=obj.id) + + +def get_likes(obj): + """Get the number of likes an object has. + + :param obj: Any Django model instance. + + :return: Number of likes or `0` if the object has no likes at all. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(obj) + + try: + return Likes.objects.get(content_type=obj_type, object_id=obj.id).count + except Likes.DoesNotExist: + return 0 + + +def get_liked(user_or_id, model): + """Get the objects liked by an user. + + :param user_or_id: :class:`~taiga.users.models.User` instance or id. + :param model: Show only objects of this kind. Can be any Django model class. + + :return: Queryset of objects representing the likes of the user. + """ + obj_type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + conditions = ('likes_like.content_type_id = %s', + '%s.id = likes_like.object_id' % model._meta.db_table, + 'likes_like.user_id = %s') + + if isinstance(user_or_id, get_user_model()): + user_id = user_or_id.id + else: + user_id = user_or_id + + return model.objects.extra(where=conditions, tables=('likes_like',), + params=(obj_type.id, user_id)) diff --git a/taiga/projects/likes/utils.py b/taiga/projects/likes/utils.py new file mode 100644 index 00000000..44035d47 --- /dev/null +++ b/taiga/projects/likes/utils.py @@ -0,0 +1,76 @@ +# Copyright (C) 2014-2015 Andrey Antukh +# Copyright (C) 2014-2015 Jesús Espino +# Copyright (C) 2014-2015 David Barragán +# Copyright (C) 2014-2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django.apps import apps + + +def attach_total_fans_to_queryset(queryset, as_field="total_fans"): + """Attach likes count to each object of the queryset. + + Because of laziness of like objects creation, this makes much simpler and more efficient to + access to liked-object number of likes. + + (The other way was to do it in the serializer with some try/except blocks and additional + queries) + + :param queryset: A Django queryset object. + :param as_field: Attach the likes-count as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = """SELECT coalesce(SUM(total_fans), 0) FROM ( + SELECT coalesce(likes_likes.count, 0) total_fans + FROM likes_likes + WHERE likes_likes.content_type_id = {type_id} + AND likes_likes.object_id = {tbl}.id + ) as e""" + + sql = sql.format(type_id=type.id, tbl=model._meta.db_table) + qs = queryset.extra(select={as_field: sql}) + return qs + + +def attach_is_fan_to_queryset(user, queryset, as_field="is_fan"): + """Attach is_like boolean to each object of the queryset. + + Because of laziness of like objects creation, this makes much simpler and more efficient to + access to likes-object and check if the curren user like it. + + (The other way was to do it in the serializer with some try/except blocks and additional + queries) + + :param user: A users.User object model + :param queryset: A Django queryset object. + :param as_field: Attach the boolean as an attribute with this name. + + :return: Queryset object with the additional `as_field` field. + """ + model = queryset.model + type = apps.get_model("contenttypes", "ContentType").objects.get_for_model(model) + sql = ("""SELECT CASE WHEN (SELECT count(*) + FROM likes_like + WHERE likes_like.content_type_id = {type_id} + AND likes_like.object_id = {tbl}.id + AND likes_like.user_id = {user_id}) > 0 + THEN TRUE + ELSE FALSE + END""") + sql = sql.format(type_id=type.id, tbl=model._meta.db_table, user_id=user.id) + qs = queryset.extra(select={as_field: sql}) + return qs diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 55605c75..c9910962 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -37,6 +37,7 @@ from taiga.projects.attachments.models import * from taiga.projects.custom_attributes.models import * from taiga.projects.custom_attributes.choices import TYPES_CHOICES, TEXT_TYPE, MULTILINE_TYPE, DATE_TYPE from taiga.projects.history.services import take_snapshot +from taiga.projects.likes.services import add_like from taiga.projects.votes.services import add_vote from taiga.events.apps import disconnect_events_signals @@ -98,8 +99,9 @@ NUM_TASKS = getattr(settings, "SAMPLE_DATA_NUM_TASKS", (0, 4)) NUM_USS_BACK = getattr(settings, "SAMPLE_DATA_NUM_USS_BACK", (8, 20)) NUM_ISSUES = getattr(settings, "SAMPLE_DATA_NUM_ISSUES", (12, 25)) NUM_ATTACHMENTS = getattr(settings, "SAMPLE_DATA_NUM_ATTACHMENTS", (0, 4)) -NUM_VOTES = getattr(settings, "SAMPLE_DATA_NUM_VOTES", (0, 3)) -NUM_PROJECT_WATCHERS = getattr(settings, "SAMPLE_DATA_NUM_PROJECT_WATCHERS", (0, 3)) +NUM_LIKES = getattr(settings, "SAMPLE_DATA_NUM_LIKES", (0, 10)) +NUM_VOTES = getattr(settings, "SAMPLE_DATA_NUM_VOTES", (0, 10)) +NUM_WATCHERS = getattr(settings, "SAMPLE_DATA_NUM_PROJECT_WATCHERS", (0, 8)) class Command(BaseCommand): sd = SampleDataHelper(seed=12345678901) @@ -220,7 +222,7 @@ class Command(BaseCommand): project.total_story_points = int(defined_points * self.sd.int(5,12) / 10) project.save() - self.create_votes(project, project) + self.create_likes(project) def create_attachment(self, obj, order): attached_file = self.sd.file_from_directory(*ATTACHMENT_SAMPLE_DATA) @@ -301,9 +303,6 @@ class Command(BaseCommand): user__isnull=False)).user bug.save() - watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - bug.add_watcher(watching_user) - take_snapshot(bug, comment=self.sd.paragraph(), user=bug.owner) @@ -315,7 +314,9 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=bug.owner) - self.create_votes(bug, project) + self.create_votes(bug) + self.create_watchers(bug) + return bug def create_task(self, project, milestone, us, min_date, max_date, closed=False): @@ -353,9 +354,6 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=task.owner) - watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - task.add_watcher(watching_user) - # Add history entry task.status=self.sd.db_object_from_queryset(project.task_statuses.all()) task.save() @@ -363,7 +361,9 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=task.owner) - self.create_votes(task, project) + self.create_votes(task) + self.create_watchers(task) + return task def create_us(self, project, milestone=None, computable_project_roles=[]): @@ -404,8 +404,6 @@ class Command(BaseCommand): user__isnull=False)).user us.save() - watching_user = self.sd.db_object_from_queryset(project.memberships.filter(user__isnull=False)).user - us.add_watcher(watching_user) take_snapshot(us, comment=self.sd.paragraph(), @@ -418,7 +416,9 @@ class Command(BaseCommand): comment=self.sd.paragraph(), user=us.owner) - self.create_votes(us, project) + self.create_votes(us) + self.create_watchers(us) + return us def create_milestone(self, project, start_date, end_date): @@ -456,9 +456,8 @@ class Command(BaseCommand): project.save() take_snapshot(project, user=project.owner) - for i in range(self.sd.int(*NUM_PROJECT_WATCHERS)): - watching_user = self.sd.db_object_from_queryset(User.objects.all()) - project.add_watcher(watching_user) + self.create_likes(project) + self.create_watchers(project) return project @@ -479,7 +478,18 @@ class Command(BaseCommand): return user - def create_votes(self, obj, project): + def create_votes(self, obj): for i in range(self.sd.int(*NUM_VOTES)): - voting_user=self.sd.db_object_from_queryset(project.members.all()) - add_vote(obj, voting_user) + user=self.sd.db_object_from_queryset(User.objects.all()) + add_vote(obj, user) + + def create_likes(self, obj): + for i in range(self.sd.int(*NUM_LIKES)): + user=self.sd.db_object_from_queryset(User.objects.all()) + add_like(obj, user) + + def create_watchers(self, obj): + for i in range(self.sd.int(*NUM_WATCHERS)): + user = self.sd.db_object_from_queryset(User.objects.all()) + obj.add_watcher(user) + diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 7a3380a8..21347615 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -43,7 +43,7 @@ from .validators import ProjectExistsValidator from .custom_attributes.serializers import UserStoryCustomAttributeSerializer from .custom_attributes.serializers import TaskCustomAttributeSerializer from .custom_attributes.serializers import IssueCustomAttributeSerializer -from .votes.mixins.serializers import FanResourceSerializerMixin +from .likes.mixins.serializers import FanResourceSerializerMixin ###################################################### ## Custom values for selectors diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index 2a4753c4..998e5442 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -24,11 +24,11 @@ from taiga.projects.history.models import HistoryEntry def _get_total_story_points(project): - return (project.total_story_points if project.total_story_points is not None else + return (project.total_story_points if project.total_story_points not in [None, 0] else sum(project.calculated_points["defined"].values())) def _get_total_milestones(project): - return (project.total_milestones if project.total_milestones is not None else + return (project.total_milestones if project.total_milestones not in [None, 0] else project.milestones.count()) def _get_milestones_stats_for_backlog(project): diff --git a/taiga/projects/votes/mixins/serializers.py b/taiga/projects/votes/mixins/serializers.py index ed17d8a3..73e1799b 100644 --- a/taiga/projects/votes/mixins/serializers.py +++ b/taiga/projects/votes/mixins/serializers.py @@ -17,19 +17,6 @@ from taiga.base.api import serializers -class FanResourceSerializerMixin(serializers.ModelSerializer): - is_fan = serializers.SerializerMethodField("get_is_fan") - total_fans = serializers.SerializerMethodField("get_total_fans") - - def get_is_fan(self, obj): - # The "is_voted" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "is_voter", False) or False - - def get_total_fans(self, obj): - # The "total_likes" attribute is attached in the get_queryset of the viewset. - return getattr(obj, "total_voters", 0) or 0 - - class VoteResourceSerializerMixin(serializers.ModelSerializer): is_voter = serializers.SerializerMethodField("get_is_voter") total_voters = serializers.SerializerMethodField("get_total_voters") @@ -39,5 +26,5 @@ class VoteResourceSerializerMixin(serializers.ModelSerializer): return getattr(obj, "is_voter", False) or False def get_total_voters(self, obj): - # The "total_likes" attribute is attached in the get_queryset of the viewset. + # The "total_voters" attribute is attached in the get_queryset of the viewset. return getattr(obj, "total_voters", 0) or 0 diff --git a/taiga/projects/votes/mixins/viewsets.py b/taiga/projects/votes/mixins/viewsets.py index 5148af7f..aa2100a0 100644 --- a/taiga/projects/votes/mixins/viewsets.py +++ b/taiga/projects/votes/mixins/viewsets.py @@ -26,7 +26,7 @@ from taiga.projects.votes import services from taiga.projects.votes.utils import attach_total_voters_to_queryset, attach_is_voter_to_queryset -class BaseVotedResource: +class VotedResourceMixin: # Note: Update get_queryset method: # def get_queryset(self): # qs = super().get_queryset() @@ -40,46 +40,24 @@ class BaseVotedResource: return qs - def _add_voter(self, permission, request, pk=None): + @detail_route(methods=["POST"]) + def upvote(self, request, pk=None): obj = self.get_object() - self.check_permissions(request, permission, obj) + self.check_permissions(request, "upvote", obj) services.add_vote(obj, user=request.user) return response.Ok() - def _remove_vote(self, permission, request, pk=None): + @detail_route(methods=["POST"]) + def downvote(self, request, pk=None): obj = self.get_object() - self.check_permissions(request, permission, obj) + self.check_permissions(request, "downvote", obj) services.remove_vote(obj, user=request.user) return response.Ok() -class LikedResourceMixin(BaseVotedResource): - # Note: objects nedd 'like' and 'unlike' permissions. - - @detail_route(methods=["POST"]) - def like(self, request, pk=None): - return self._add_voter("like", request, pk) - - @detail_route(methods=["POST"]) - def unlike(self, request, pk=None): - return self._remove_vote("unlike", request, pk) - - -class VotedResourceMixin(BaseVotedResource): - # Note: objects nedd 'upvote' and 'downvote' permissions. - - @detail_route(methods=["POST"]) - def upvote(self, request, pk=None): - return self._add_voter("upvote", request, pk) - - @detail_route(methods=["POST"]) - def downvote(self, request, pk=None): - return self._remove_vote("downvote", request, pk) - - -class BaseVotersViewSetMixin: +class VotersViewSetMixin: # Is a ModelListViewSet with two required params: permission_classes and resource_model serializer_class = serializers.VoterSerializer list_serializer_class = serializers.VoterSerializer @@ -112,11 +90,3 @@ class BaseVotersViewSetMixin: def get_queryset(self): resource = self.resource_model.objects.get(pk=self.kwargs.get("resource_id")) return services.get_voters(resource) - - -class VotersViewSetMixin(BaseVotersViewSetMixin): - pass - - -class FansViewSetMixin(BaseVotersViewSetMixin): - pass diff --git a/taiga/users/api.py b/taiga/users/api.py index 4c44422c..d72d021d 100644 --- a/taiga/users/api.py +++ b/taiga/users/api.py @@ -77,96 +77,64 @@ class UsersViewSet(ModelCrudViewSet): return response.Ok(serializer.data) - @list_route(methods=["GET"]) - def by_username(self, request, *args, **kwargs): - username = request.QUERY_PARAMS.get("username", None) - return self.retrieve(request, username=username) - def retrieve(self, request, *args, **kwargs): self.object = get_object_or_404(models.User, **kwargs) self.check_permissions(request, 'retrieve', self.object) serializer = self.get_serializer(self.object) return response.Ok(serializer.data) - @detail_route(methods=["GET"]) - def contacts(self, request, *args, **kwargs): - user = get_object_or_404(models.User, **kwargs) - self.check_permissions(request, 'contacts', user) + #TODO: commit_on_success + def partial_update(self, request, *args, **kwargs): + """ + We must detect if the user is trying to change his email so we can + save that value and generate a token that allows him to validate it in + the new email account + """ + user = self.get_object() + self.check_permissions(request, "update", user) - self.object_list = user_filters.ContactsFilterBackend().filter_queryset( - user, request, self.get_queryset(), self).extra( - select={"complete_user_name":"concat(full_name, username)"}).order_by("complete_user_name") + ret = super().partial_update(request, *args, **kwargs) - page = self.paginate_queryset(self.object_list) - if page is not None: - serializer = self.serializer_class(page.object_list, many=True) - else: - serializer = self.serializer_class(self.object_list, many=True) + new_email = request.DATA.get('email', None) + if new_email is not None: + valid_new_email = True + duplicated_email = models.User.objects.filter(email = new_email).exists() - return response.Ok(serializer.data) + try: + validate_email(new_email) + except ValidationError: + valid_new_email = False - @detail_route(methods=["GET"]) - def stats(self, request, *args, **kwargs): - user = get_object_or_404(models.User, **kwargs) - self.check_permissions(request, "stats", user) - return response.Ok(services.get_stats_for_user(user, request.user)) + valid_new_email = valid_new_email and new_email != request.user.email + if duplicated_email: + raise exc.WrongArguments(_("Duplicated email")) + elif not valid_new_email: + raise exc.WrongArguments(_("Not valid email")) - def _serialize_liked_content(self, elem, **kwargs): - if elem.get("type") == "project": - serializer = serializers.FanSerializer - else: - serializer = serializers.VotedSerializer + #We need to generate a token for the email + request.user.email_token = str(uuid.uuid1()) + request.user.new_email = new_email + request.user.save(update_fields=["email_token", "new_email"]) + email = mail_builder.change_email(request.user.new_email, {"user": request.user, + "lang": request.user.lang}) + email.send() - return serializer(elem, **kwargs) + return ret + def destroy(self, request, pk=None): + user = self.get_object() + self.check_permissions(request, "destroy", user) + stream = request.stream + request_data = stream is not None and stream.GET or None + user_cancel_account_signal.send(sender=user.__class__, user=user, request_data=request_data) + user.cancel() + return response.NoContent() - @detail_route(methods=["GET"]) - def watched(self, request, *args, **kwargs): - for_user = get_object_or_404(models.User, **kwargs) - from_user = request.user - self.check_permissions(request, 'watched', for_user) - filters = { - "type": request.GET.get("type", None), - "q": request.GET.get("q", None), - } - - self.object_list = services.get_watched_list(for_user, from_user, **filters) - page = self.paginate_queryset(self.object_list) - elements = page.object_list if page is not None else self.object_list - - extra_args = { - "user_votes": services.get_voted_content_for_user(request.user), - "user_watching": services.get_watched_content_for_user(request.user), - } - - response_data = [self._serialize_liked_content(elem, **extra_args).data for elem in elements] - return response.Ok(response_data) - - - @detail_route(methods=["GET"]) - def liked(self, request, *args, **kwargs): - for_user = get_object_or_404(models.User, **kwargs) - from_user = request.user - self.check_permissions(request, 'liked', for_user) - filters = { - "type": request.GET.get("type", None), - "q": request.GET.get("q", None), - } - - self.object_list = services.get_voted_list(for_user, from_user, **filters) - page = self.paginate_queryset(self.object_list) - elements = page.object_list if page is not None else self.object_list - - extra_args = { - "user_votes": services.get_voted_content_for_user(request.user), - "user_watching": services.get_watched_content_for_user(request.user), - } - - response_data = [self._serialize_liked_content(elem, **extra_args).data for elem in elements] - - return response.Ok(response_data) - + @list_route(methods=["GET"]) + def by_username(self, request, *args, **kwargs): + username = request.QUERY_PARAMS.get("username", None) + return self.retrieve(request, username=username) @list_route(methods=["POST"]) def password_recovery(self, request, pk=None): @@ -278,45 +246,6 @@ class UsersViewSet(ModelCrudViewSet): user_data = self.admin_serializer_class(request.user).data return response.Ok(user_data) - #TODO: commit_on_success - def partial_update(self, request, *args, **kwargs): - """ - We must detect if the user is trying to change his email so we can - save that value and generate a token that allows him to validate it in - the new email account - """ - user = self.get_object() - self.check_permissions(request, "update", user) - - ret = super(UsersViewSet, self).partial_update(request, *args, **kwargs) - - new_email = request.DATA.get('email', None) - if new_email is not None: - valid_new_email = True - duplicated_email = models.User.objects.filter(email = new_email).exists() - - try: - validate_email(new_email) - except ValidationError: - valid_new_email = False - - valid_new_email = valid_new_email and new_email != request.user.email - - if duplicated_email: - raise exc.WrongArguments(_("Duplicated email")) - elif not valid_new_email: - raise exc.WrongArguments(_("Not valid email")) - - #We need to generate a token for the email - request.user.email_token = str(uuid.uuid1()) - request.user.new_email = new_email - request.user.save(update_fields=["email_token", "new_email"]) - email = mail_builder.change_email(request.user.new_email, {"user": request.user, - "lang": request.user.lang}) - email.send() - - return ret - @list_route(methods=["POST"]) def change_email(self, request, pk=None): """ @@ -373,15 +302,108 @@ class UsersViewSet(ModelCrudViewSet): user.cancel() return response.NoContent() - def destroy(self, request, pk=None): - user = self.get_object() - self.check_permissions(request, "destroy", user) - stream = request.stream - request_data = stream is not None and stream.GET or None - user_cancel_account_signal.send(sender=user.__class__, user=user, request_data=request_data) - user.cancel() - return response.NoContent() + @detail_route(methods=["GET"]) + def contacts(self, request, *args, **kwargs): + user = get_object_or_404(models.User, **kwargs) + self.check_permissions(request, 'contacts', user) + self.object_list = user_filters.ContactsFilterBackend().filter_queryset( + user, request, self.get_queryset(), self).extra( + select={"complete_user_name":"concat(full_name, username)"}).order_by("complete_user_name") + + page = self.paginate_queryset(self.object_list) + if page is not None: + serializer = self.serializer_class(page.object_list, many=True) + else: + serializer = self.serializer_class(self.object_list, many=True) + + return response.Ok(serializer.data) + + @detail_route(methods=["GET"]) + def stats(self, request, *args, **kwargs): + user = get_object_or_404(models.User, **kwargs) + self.check_permissions(request, "stats", user) + return response.Ok(services.get_stats_for_user(user, request.user)) + + @detail_route(methods=["GET"]) + def watched(self, request, *args, **kwargs): + for_user = get_object_or_404(models.User, **kwargs) + from_user = request.user + self.check_permissions(request, 'watched', for_user) + filters = { + "type": request.GET.get("type", None), + "q": request.GET.get("q", None), + } + + self.object_list = services.get_watched_list(for_user, from_user, **filters) + page = self.paginate_queryset(self.object_list) + elements = page.object_list if page is not None else self.object_list + + extra_args_liked = { + "user_watching": services.get_watched_content_for_user(request.user), + "user_likes": services.get_liked_content_for_user(request.user), + } + + extra_args_voted = { + "user_watching": services.get_watched_content_for_user(request.user), + "user_votes": services.get_voted_content_for_user(request.user), + } + + response_data = [] + for elem in elements: + if elem["type"] == "project": + # projects are liked objects + response_data.append(serializers.LikedObjectSerializer(elem, **extra_args_liked).data ) + else: + # stories, tasks and issues are voted objects + response_data.append(serializers.VotedObjectSerializer(elem, **extra_args_voted).data ) + + return response.Ok(response_data) + + @detail_route(methods=["GET"]) + def liked(self, request, *args, **kwargs): + for_user = get_object_or_404(models.User, **kwargs) + from_user = request.user + self.check_permissions(request, 'liked', for_user) + filters = { + "q": request.GET.get("q", None), + } + + self.object_list = services.get_liked_list(for_user, from_user, **filters) + page = self.paginate_queryset(self.object_list) + elements = page.object_list if page is not None else self.object_list + + extra_args = { + "user_watching": services.get_watched_content_for_user(request.user), + "user_likes": services.get_liked_content_for_user(request.user), + } + + response_data = [serializers.LikedObjectSerializer(elem, **extra_args).data for elem in elements] + + return response.Ok(response_data) + + @detail_route(methods=["GET"]) + def voted(self, request, *args, **kwargs): + for_user = get_object_or_404(models.User, **kwargs) + from_user = request.user + self.check_permissions(request, 'liked', for_user) + filters = { + "type": request.GET.get("type", None), + "q": request.GET.get("q", None), + } + + self.object_list = services.get_voted_list(for_user, from_user, **filters) + page = self.paginate_queryset(self.object_list) + elements = page.object_list if page is not None else self.object_list + + extra_args = { + "user_watching": services.get_watched_content_for_user(request.user), + "user_votes": services.get_voted_content_for_user(request.user), + } + + response_data = [serializers.VotedObjectSerializer(elem, **extra_args).data for elem in elements] + + return response.Ok(response_data) ###################################################### ## Role diff --git a/taiga/users/permissions.py b/taiga/users/permissions.py index 5426d3f5..de72dd85 100644 --- a/taiga/users/permissions.py +++ b/taiga/users/permissions.py @@ -47,6 +47,7 @@ class UserPermission(TaigaResourcePermission): change_email_perms = AllowAny() contacts_perms = AllowAny() liked_perms = AllowAny() + voted_perms = AllowAny() watched_perms = AllowAny() diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 24e5eb82..2a7000fd 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -159,7 +159,7 @@ class ProjectRoleSerializer(serializers.ModelSerializer): ###################################################### -class LikeSerializer(serializers.Serializer): +class HighLightedContentSerializer(serializers.Serializer): type = serializers.CharField() id = serializers.IntegerField() ref = serializers.IntegerField() @@ -174,9 +174,6 @@ class LikeSerializer(serializers.Serializer): created_date = serializers.DateTimeField() is_private = serializers.SerializerMethodField("get_is_private") - is_watcher = serializers.SerializerMethodField("get_is_watcher") - total_watchers = serializers.IntegerField() - project = serializers.SerializerMethodField("get_project") project_name = serializers.SerializerMethodField("get_project_name") project_slug = serializers.SerializerMethodField("get_project_slug") @@ -186,13 +183,15 @@ class LikeSerializer(serializers.Serializer): assigned_to_full_name = serializers.CharField() assigned_to_photo = serializers.SerializerMethodField("get_photo") + is_watcher = serializers.SerializerMethodField("get_is_watcher") + total_watchers = serializers.IntegerField() + def __init__(self, *args, **kwargs): # Don't pass the extra ids args up to the superclass - self.user_votes = kwargs.pop("user_votes", {}) self.user_watching = kwargs.pop("user_watching", {}) # Instantiate the superclass normally - super(LikeSerializer, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) def _none_if_project(self, obj, property): type = obj.get("type", "") @@ -226,9 +225,6 @@ class LikeSerializer(serializers.Serializer): def get_project_is_private(self, obj): return self._none_if_project(obj, "project_is_private") - def get_is_watcher(self, obj): - return obj["id"] in self.user_watching.get(obj["type"], []) - def get_photo(self, obj): type = obj.get("type", "") if type == "project": @@ -242,18 +238,35 @@ class LikeSerializer(serializers.Serializer): tags = obj.get("tags", []) return [{"name": tc[0], "color": tc[1]} for tc in obj.get("tags_colors", []) if tc[0] in tags] + def get_is_watcher(self, obj): + return obj["id"] in self.user_watching.get(obj["type"], []) -class FanSerializer(LikeSerializer): +class LikedObjectSerializer(HighLightedContentSerializer): is_fan = serializers.SerializerMethodField("get_is_fan") - total_fans = serializers.IntegerField(source="total_voters") + total_fans = serializers.IntegerField() + + def __init__(self, *args, **kwargs): + # Don't pass the extra ids args up to the superclass + self.user_likes = kwargs.pop("user_likes", {}) + + # Instantiate the superclass normally + super().__init__(*args, **kwargs) def get_is_fan(self, obj): - return obj["id"] in self.user_votes.get(obj["type"], []) + return obj["id"] in self.user_likes.get(obj["type"], []) -class VotedSerializer(LikeSerializer): + +class VotedObjectSerializer(HighLightedContentSerializer): is_voter = serializers.SerializerMethodField("get_is_voter") total_voters = serializers.IntegerField() + def __init__(self, *args, **kwargs): + # Don't pass the extra ids args up to the superclass + self.user_votes = kwargs.pop("user_votes", {}) + + # Instantiate the superclass normally + super().__init__(*args, **kwargs) + def get_is_voter(self, obj): return obj["id"] in self.user_votes.get(obj["type"], []) diff --git a/taiga/users/services.py b/taiga/users/services.py index 534b9b6e..4542d22b 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -149,6 +149,23 @@ def get_stats_for_user(from_user, by_user): return project_stats +def get_liked_content_for_user(user): + """Returns a dict where: + - The key is the content_type model + - The values are list of id's of the different objects liked by the user + """ + if user.is_anonymous(): + return {} + + user_likes = {} + for (ct_model, object_id) in user.likes.values_list("content_type__model", "object_id"): + list = user_likes.get(ct_model, []) + list.append(object_id) + user_likes[ct_model] = list + + return user_likes + + def get_voted_content_for_user(user): """Returns a dict where: - The key is the content_type model @@ -190,11 +207,12 @@ def get_watched_content_for_user(user): def _build_watched_sql_for_projects(for_user): sql = """ - SELECT projects_project.id AS id, null AS ref, 'project' AS type, + SELECT projects_project.id AS id, null::integer AS ref, 'project'::text AS type, tags, notifications_notifypolicy.project_id AS object_id, projects_project.id AS project, - slug AS slug, projects_project.name AS name, null AS subject, - notifications_notifypolicy.created_at as created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, null AS assigned_to, - null as status, null as status_color + slug, projects_project.name, null::text AS subject, + notifications_notifypolicy.created_at as created_date, + coalesce(watchers, 0) AS total_watchers, coalesce(likes_likes.count, 0) AS total_fans, null::integer AS total_voters, + null::integer AS assigned_to, null::text as status, null::text as status_color FROM notifications_notifypolicy INNER JOIN projects_project ON (projects_project.id = notifications_notifypolicy.project_id) @@ -204,8 +222,8 @@ def _build_watched_sql_for_projects(for_user): GROUP BY project_id ) type_watchers ON projects_project.id = type_watchers.project_id - LEFT JOIN votes_votes - ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) + LEFT JOIN likes_likes + ON (projects_project.id = likes_likes.object_id AND {project_content_type_id} = likes_likes.content_type_id) WHERE notifications_notifypolicy.user_id = {for_user_id} """ sql = sql.format( @@ -215,30 +233,32 @@ def _build_watched_sql_for_projects(for_user): return sql -def _build_voted_sql_for_projects(for_user): +def _build_liked_sql_for_projects(for_user): sql = """ - SELECT projects_project.id AS id, null AS ref, 'project' AS type, - tags, votes_vote.object_id AS object_id, projects_project.id AS project, - slug AS slug, projects_project.name AS name, null AS subject, - votes_vote.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, null AS assigned_to, - null as status, null as status_color - FROM votes_vote + SELECT projects_project.id AS id, null::integer AS ref, 'project'::text AS type, + tags, likes_like.object_id AS object_id, projects_project.id AS project, + slug, projects_project.name, null::text AS subject, + likes_like.created_date, + coalesce(watchers, 0) AS total_watchers, coalesce(likes_likes.count, 0) AS total_fans, + null::integer AS assigned_to, null::text as status, null::text as status_color + FROM likes_like INNER JOIN projects_project - ON (projects_project.id = votes_vote.object_id) + ON (projects_project.id = likes_like.object_id) LEFT JOIN (SELECT project_id, count(*) watchers FROM notifications_notifypolicy WHERE notifications_notifypolicy.notify_level != {ignore_notify_level} GROUP BY project_id ) type_watchers ON projects_project.id = type_watchers.project_id - LEFT JOIN votes_votes - ON (projects_project.id = votes_votes.object_id AND {project_content_type_id} = votes_votes.content_type_id) - WHERE votes_vote.user_id = {for_user_id} AND {project_content_type_id} = votes_vote.content_type_id + LEFT JOIN likes_likes + ON (projects_project.id = likes_likes.object_id AND {project_content_type_id} = likes_likes.content_type_id) + WHERE likes_like.user_id = {for_user_id} AND {project_content_type_id} = likes_like.content_type_id """ sql = sql.format( for_user_id=for_user.id, ignore_notify_level=NotifyLevel.ignore, project_content_type_id=ContentType.objects.get(app_label="projects", model="project").id) + return sql @@ -249,8 +269,9 @@ def _build_sql_for_type(for_user, type, table_name, action_table, ref_column="re SELECT {table_name}.id AS id, {ref_column} AS ref, '{type}' AS type, tags, {action_table}.object_id AS object_id, {table_name}.{project_column} AS project, {slug_column} AS slug, null AS name, {subject_column} AS subject, - {action_table}.created_date, coalesce(watchers, 0) as total_watchers, coalesce(votes_votes.count, 0) total_voters, {assigned_to_column} AS assigned_to, - projects_{type}status.name as status, projects_{type}status.color as status_color + {action_table}.created_date, + coalesce(watchers, 0) AS total_watchers, null::integer AS total_fans, coalesce(votes_votes.count, 0) AS total_voters, + {assigned_to_column} AS assigned_to, projects_{type}status.name as status, projects_{type}status.color as status_color FROM {action_table} INNER JOIN django_content_type ON ({action_table}.content_type_id = django_content_type.id AND django_content_type.model = '{type}') @@ -272,7 +293,7 @@ def _build_sql_for_type(for_user, type, table_name, action_table, ref_column="re return sql -def _get_favourites_list(for_user, from_user, action_table, project_sql_builder, type=None, q=None): +def get_watched_list(for_user, from_user, type=None, q=None): filters_sql = "" and_needed = False @@ -348,10 +369,10 @@ def _get_favourites_list(for_user, from_user, action_table, project_sql_builder, for_user_id=for_user.id, from_user_id=from_user_id, filters_sql=filters_sql, - userstories_sql=_build_sql_for_type(for_user, "userstory", "userstories_userstory", action_table, slug_column="null"), - tasks_sql=_build_sql_for_type(for_user, "task", "tasks_task", action_table, slug_column="null"), - issues_sql=_build_sql_for_type(for_user, "issue", "issues_issue", action_table, slug_column="null"), - projects_sql=project_sql_builder(for_user)) + userstories_sql=_build_sql_for_type(for_user, "userstory", "userstories_userstory", "notifications_watched", slug_column="null"), + tasks_sql=_build_sql_for_type(for_user, "task", "tasks_task", "notifications_watched", slug_column="null"), + issues_sql=_build_sql_for_type(for_user, "issue", "issues_issue", "notifications_watched", slug_column="null"), + projects_sql=_build_watched_sql_for_projects(for_user)) cursor = connection.cursor() cursor.execute(sql) @@ -363,9 +384,167 @@ def _get_favourites_list(for_user, from_user, action_table, project_sql_builder, ] -def get_watched_list(for_user, from_user, type=None, q=None): - return _get_favourites_list(for_user, from_user, "notifications_watched", _build_watched_sql_for_projects, type=type, q=q) +def get_liked_list(for_user, from_user, type=None, q=None): + filters_sql = "" + and_needed = False + + if type: + filters_sql += " AND type = '{type}' ".format(type=type) + + if q: + filters_sql += """ AND ( + to_tsvector('english_nostop', coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('english_nostop', '{q}') + ) + """.format(q=to_tsquery(q)) + + sql = """ + -- BEGIN Basic info: we need to mix info from different tables and denormalize it + SELECT entities.*, + projects_project.name as project_name, projects_project.description as description, projects_project.slug as project_slug, projects_project.is_private as project_is_private, + projects_project.tags_colors, + users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email + FROM ( + {projects_sql} + ) as entities + -- END Basic info + + -- BEGIN Project info + LEFT JOIN projects_project + ON (entities.project = projects_project.id) + -- END Project info + + -- BEGIN Assigned to user info + LEFT JOIN users_user + ON (assigned_to = users_user.id) + -- END Assigned to user info + + -- BEGIN Permissions checking + LEFT JOIN projects_membership + -- Here we check the memberbships from the user requesting the info + ON (projects_membership.user_id = {from_user_id} AND projects_membership.project_id = entities.project) + + LEFT JOIN users_role + ON (entities.project = users_role.project_id AND users_role.id = projects_membership.role_id) + + WHERE + -- public project + ( + projects_project.is_private = false + OR( + -- private project where the view_ permission is included in the user role for that project or in the anon permissions + projects_project.is_private = true + AND( + 'view_project' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions)) + ) + )) + -- END Permissions checking + {filters_sql} + + ORDER BY entities.created_date DESC; + """ + + from_user_id = -1 + if not from_user.is_anonymous(): + from_user_id = from_user.id + + sql = sql.format( + for_user_id=for_user.id, + from_user_id=from_user_id, + filters_sql=filters_sql, + projects_sql=_build_liked_sql_for_projects(for_user)) + + cursor = connection.cursor() + cursor.execute(sql) + + desc = cursor.description + return [ + dict(zip([col[0] for col in desc], row)) + for row in cursor.fetchall() + ] def get_voted_list(for_user, from_user, type=None, q=None): - return _get_favourites_list(for_user, from_user, "votes_vote", _build_voted_sql_for_projects, type=type, q=q) + filters_sql = "" + and_needed = False + + if type: + filters_sql += " AND type = '{type}' ".format(type=type) + + if q: + filters_sql += """ AND ( + to_tsvector('english_nostop', coalesce(subject,'') || ' ' ||coalesce(entities.name,'') || ' ' ||coalesce(to_char(ref, '999'),'')) @@ to_tsquery('english_nostop', '{q}') + ) + """.format(q=to_tsquery(q)) + + sql = """ + -- BEGIN Basic info: we need to mix info from different tables and denormalize it + SELECT entities.*, + projects_project.name as project_name, projects_project.description as description, projects_project.slug as project_slug, projects_project.is_private as project_is_private, + projects_project.tags_colors, + users_user.username assigned_to_username, users_user.full_name assigned_to_full_name, users_user.photo assigned_to_photo, users_user.email assigned_to_email + FROM ( + {userstories_sql} + UNION + {tasks_sql} + UNION + {issues_sql} + ) as entities + -- END Basic info + + -- BEGIN Project info + LEFT JOIN projects_project + ON (entities.project = projects_project.id) + -- END Project info + + -- BEGIN Assigned to user info + LEFT JOIN users_user + ON (assigned_to = users_user.id) + -- END Assigned to user info + + -- BEGIN Permissions checking + LEFT JOIN projects_membership + -- Here we check the memberbships from the user requesting the info + ON (projects_membership.user_id = {from_user_id} AND projects_membership.project_id = entities.project) + + LEFT JOIN users_role + ON (entities.project = users_role.project_id AND users_role.id = projects_membership.role_id) + + WHERE + -- public project + ( + projects_project.is_private = false + OR( + -- private project where the view_ permission is included in the user role for that project or in the anon permissions + projects_project.is_private = true + AND( + (entities.type = 'issue' AND 'view_issues' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + OR (entities.type = 'task' AND 'view_tasks' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + OR (entities.type = 'userstory' AND 'view_us' = ANY (array_cat(users_role.permissions, projects_project.anon_permissions))) + ) + )) + -- END Permissions checking + {filters_sql} + + ORDER BY entities.created_date DESC; + """ + + from_user_id = -1 + if not from_user.is_anonymous(): + from_user_id = from_user.id + + sql = sql.format( + for_user_id=for_user.id, + from_user_id=from_user_id, + filters_sql=filters_sql, + userstories_sql=_build_sql_for_type(for_user, "userstory", "userstories_userstory", "votes_vote", slug_column="null"), + tasks_sql=_build_sql_for_type(for_user, "task", "tasks_task", "votes_vote", slug_column="null"), + issues_sql=_build_sql_for_type(for_user, "issue", "issues_issue", "votes_vote", slug_column="null")) + + cursor = connection.cursor() + cursor.execute(sql) + + desc = cursor.description + return [ + dict(zip([col[0] for col in desc], row)) + for row in cursor.fetchall() + ] diff --git a/tests/factories.py b/tests/factories.py index 532faefe..c44167c5 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -412,14 +412,23 @@ class IssueCustomAttributesValuesFactory(Factory): issue = factory.SubFactory("tests.factories.IssueFactory") -# class FanFactory(Factory): -# project = factory.SubFactory("tests.factories.ProjectFactory") -# user = factory.SubFactory("tests.factories.UserFactory") +class LikeFactory(Factory): + class Meta: + model = "likes.Like" + strategy = factory.CREATE_STRATEGY + + content_type = factory.SubFactory("tests.factories.ContentTypeFactory") + object_id = factory.Sequence(lambda n: n) + user = factory.SubFactory("tests.factories.UserFactory") -# class StarsFactory(Factory): -# project = factory.SubFactory("tests.factories.ProjectFactory") -# count = 0 +class LikesFactory(Factory): + class Meta: + model = "likes.Likes" + strategy = factory.CREATE_STRATEGY + + content_type = factory.SubFactory("tests.factories.ContentTypeFactory") + object_id = factory.Sequence(lambda n: n) class VoteFactory(Factory): diff --git a/tests/integration/resources_permissions/test_projects_resource.py b/tests/integration/resources_permissions/test_projects_resource.py index 26132062..e604ac7f 100644 --- a/tests/integration/resources_permissions/test_projects_resource.py +++ b/tests/integration/resources_permissions/test_projects_resource.py @@ -70,16 +70,16 @@ def data(): project_ct = ContentType.objects.get_for_model(Project) - f.VoteFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_member_with_perms) - f.VoteFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_owner) - f.VoteFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_member_with_perms) - f.VoteFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_owner) - f.VoteFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_member_with_perms) - f.VoteFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_owner) + f.LikeFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_member_with_perms) + f.LikeFactory(content_type=project_ct, object_id=m.public_project.pk, user=m.project_owner) + f.LikeFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_member_with_perms) + f.LikeFactory(content_type=project_ct, object_id=m.private_project1.pk, user=m.project_owner) + f.LikeFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_member_with_perms) + f.LikeFactory(content_type=project_ct, object_id=m.private_project2.pk, user=m.project_owner) - f.VotesFactory(content_type=project_ct, object_id=m.public_project.pk, count=2) - f.VotesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2) - f.VotesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2) + f.LikesFactory(content_type=project_ct, object_id=m.public_project.pk, count=2) + f.LikesFactory(content_type=project_ct, object_id=m.private_project1.pk, count=2) + f.LikesFactory(content_type=project_ct, object_id=m.private_project2.pk, count=2) return m diff --git a/tests/integration/resources_permissions/test_users_resources.py b/tests/integration/resources_permissions/test_users_resources.py index a78d270d..a6604988 100644 --- a/tests/integration/resources_permissions/test_users_resources.py +++ b/tests/integration/resources_permissions/test_users_resources.py @@ -311,3 +311,15 @@ def test_user_list_liked(client, data): ] results = helper_test_http_method(client, 'get', url, None, users) assert results == [200, 200, 200, 200] + + +def test_user_list_voted(client, data): + url = reverse('users-voted', kwargs={"pk": data.registered_user.pk}) + users = [ + None, + data.registered_user, + data.other_user, + data.superuser, + ] + results = helper_test_http_method(client, 'get', url, None, users) + assert results == [200, 200, 200, 200] diff --git a/tests/integration/test_fan_projects.py b/tests/integration/test_fan_projects.py new file mode 100644 index 00000000..6f60e50b --- /dev/null +++ b/tests/integration/test_fan_projects.py @@ -0,0 +1,123 @@ +# Copyright (C) 2015 Andrey Antukh +# Copyright (C) 2015 Jesús Espino +# Copyright (C) 2015 David Barragán +# Copyright (C) 2015 Anler Hernández +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import pytest +from django.core.urlresolvers import reverse + +from .. import factories as f + +pytestmark = pytest.mark.django_db + + +def test_like_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-like", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_unlike_project(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-unlike", args=(project.id,)) + + client.login(user) + response = client.post(url) + + assert response.status_code == 200 + + +def test_list_project_fans(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.LikeFactory.create(content_object=project, user=user) + url = reverse("project-fans-list", args=(project.id,)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data[0]['id'] == user.id + + +def test_get_project_fan(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + like = f.LikeFactory.create(content_object=project, user=user) + url = reverse("project-fans-detail", args=(project.id, like.user.id)) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['id'] == like.user.id + + +def test_get_project_total_fans(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + url = reverse("projects-detail", args=(project.id,)) + + f.LikesFactory.create(content_object=project, count=5) + + client.login(user) + response = client.get(url) + + assert response.status_code == 200 + assert response.data['total_fans'] == 5 + + +def test_get_project_is_fan(client): + user = f.UserFactory.create() + project = f.create_project(owner=user) + f.MembershipFactory.create(project=project, user=user, is_owner=True) + f.LikesFactory.create(content_object=project) + url_detail = reverse("projects-detail", args=(project.id,)) + url_like = reverse("projects-like", args=(project.id,)) + url_unlike = reverse("projects-unlike", args=(project.id,)) + + client.login(user) + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['total_fans'] == 0 + assert response.data['is_fan'] == False + + response = client.post(url_like) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['total_fans'] == 1 + assert response.data['is_fan'] == True + + response = client.post(url_unlike) + assert response.status_code == 200 + + response = client.get(url_detail) + assert response.status_code == 200 + assert response.data['total_fans'] == 0 + assert response.data['is_fan'] == False diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index a9d4c1cd..1a5959e0 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -9,10 +9,10 @@ from .. import factories as f from taiga.base.utils import json from taiga.users import models -from taiga.users.serializers import FanSerializer, VotedSerializer +from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS -from taiga.users.services import get_watched_list, get_voted_list +from taiga.users.services import get_watched_list, get_voted_list, get_liked_list from easy_thumbnails.files import generate_all_aliases, get_thumbnailer @@ -377,6 +377,25 @@ def test_get_watched_list(): assert len(get_watched_list(fav_user, viewer_user, q="unexisting text")) == 0 +def test_get_liked_list(): + fan_user = f.UserFactory() + viewer_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fan_user) + content_type = ContentType.objects.get_for_model(project) + f.LikeFactory(content_type=content_type, object_id=project.id, user=fan_user) + f.LikesFactory(content_type=content_type, object_id=project.id, count=1) + + assert len(get_liked_list(fan_user, viewer_user)) == 1 + assert len(get_liked_list(fan_user, viewer_user, type="project")) == 1 + assert len(get_liked_list(fan_user, viewer_user, type="unknown")) == 0 + + assert len(get_liked_list(fan_user, viewer_user, q="project")) == 1 + assert len(get_liked_list(fan_user, viewer_user, q="unexisting text")) == 0 + + def test_get_voted_list(): fav_user = f.UserFactory() viewer_user = f.UserFactory() @@ -384,9 +403,6 @@ def test_get_voted_list(): project = f.ProjectFactory(is_private=False, name="Testing project") role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) membership = f.MembershipFactory(project=project, role=role, user=fav_user) - content_type = ContentType.objects.get_for_model(project) - f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) - f.VotesFactory(content_type=content_type, object_id=project.id, count=1) user_story = f.UserStoryFactory(project=project, subject="Testing user story") content_type = ContentType.objects.get_for_model(user_story) @@ -403,8 +419,7 @@ def test_get_voted_list(): f.VoteFactory(content_type=content_type, object_id=issue.id, user=fav_user) f.VotesFactory(content_type=content_type, object_id=issue.id, count=1) - assert len(get_voted_list(fav_user, viewer_user)) == 4 - assert len(get_voted_list(fav_user, viewer_user, type="project")) == 1 + assert len(get_voted_list(fav_user, viewer_user)) == 3 assert len(get_voted_list(fav_user, viewer_user, type="userstory")) == 1 assert len(get_voted_list(fav_user, viewer_user, type="task")) == 1 assert len(get_voted_list(fav_user, viewer_user, type="issue")) == 1 @@ -423,7 +438,8 @@ def test_get_watched_list_valid_info_for_project(): project.add_watcher(fav_user) raw_project_watch_info = get_watched_list(fav_user, viewer_user)[0] - project_watch_info = FanSerializer(raw_project_watch_info).data + + project_watch_info = LikedObjectSerializer(raw_project_watch_info).data assert project_watch_info["type"] == "project" assert project_watch_info["id"] == project.id @@ -454,46 +470,46 @@ def test_get_watched_list_valid_info_for_project(): assert project_watch_info["assigned_to_photo"] == None -def test_get_voted_list_valid_info_for_project(): - fav_user = f.UserFactory() +def test_get_liked_list_valid_info(): + fan_user = f.UserFactory() viewer_user = f.UserFactory() project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) content_type = ContentType.objects.get_for_model(project) - vote = f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) - f.VotesFactory(content_type=content_type, object_id=project.id, count=1) + like = f.LikeFactory(content_type=content_type, object_id=project.id, user=fan_user) + f.LikesFactory(content_type=content_type, object_id=project.id, count=1) - raw_project_vote_info = get_voted_list(fav_user, viewer_user)[0] - project_vote_info = FanSerializer(raw_project_vote_info).data + raw_project_like_info = get_liked_list(fan_user, viewer_user)[0] + project_like_info = LikedObjectSerializer(raw_project_like_info).data - assert project_vote_info["type"] == "project" - assert project_vote_info["id"] == project.id - assert project_vote_info["ref"] == None - assert project_vote_info["slug"] == project.slug - assert project_vote_info["name"] == project.name - assert project_vote_info["subject"] == None - assert project_vote_info["description"] == project.description - assert project_vote_info["assigned_to"] == None - assert project_vote_info["status"] == None - assert project_vote_info["status_color"] == None + assert project_like_info["type"] == "project" + assert project_like_info["id"] == project.id + assert project_like_info["ref"] == None + assert project_like_info["slug"] == project.slug + assert project_like_info["name"] == project.name + assert project_like_info["subject"] == None + assert project_like_info["description"] == project.description + assert project_like_info["assigned_to"] == None + assert project_like_info["status"] == None + assert project_like_info["status_color"] == None - tags_colors = {tc["name"]:tc["color"] for tc in project_vote_info["tags_colors"]} + tags_colors = {tc["name"]:tc["color"] for tc in project_like_info["tags_colors"]} assert "test" in tags_colors assert "tag" in tags_colors - assert project_vote_info["is_private"] == project.is_private + assert project_like_info["is_private"] == project.is_private - assert project_vote_info["is_fan"] == False - assert project_vote_info["is_watcher"] == False - assert project_vote_info["total_watchers"] == 0 - assert project_vote_info["total_fans"] == 1 - assert project_vote_info["project"] == None - assert project_vote_info["project_name"] == None - assert project_vote_info["project_slug"] == None - assert project_vote_info["project_is_private"] == None - assert project_vote_info["assigned_to_username"] == None - assert project_vote_info["assigned_to_full_name"] == None - assert project_vote_info["assigned_to_photo"] == None + assert project_like_info["is_fan"] == False + assert project_like_info["is_watcher"] == False + assert project_like_info["total_watchers"] == 0 + assert project_like_info["total_fans"] == 1 + assert project_like_info["project"] == None + assert project_like_info["project_name"] == None + assert project_like_info["project_slug"] == None + assert project_like_info["project_is_private"] == None + assert project_like_info["assigned_to_username"] == None + assert project_like_info["assigned_to_full_name"] == None + assert project_like_info["assigned_to_photo"] == None def test_get_watched_list_valid_info_for_not_project_types(): @@ -517,7 +533,7 @@ def test_get_watched_list_valid_info_for_not_project_types(): instance.add_watcher(fav_user) raw_instance_watch_info = get_watched_list(fav_user, viewer_user, type=object_type)[0] - instance_watch_info = VotedSerializer(raw_instance_watch_info).data + instance_watch_info = VotedObjectSerializer(raw_instance_watch_info).data assert instance_watch_info["type"] == object_type assert instance_watch_info["id"] == instance.id @@ -548,7 +564,7 @@ def test_get_watched_list_valid_info_for_not_project_types(): assert instance_watch_info["assigned_to_photo"] != "" -def test_get_voted_list_valid_info_for_not_project_types(): +def test_get_voted_list_valid_info(): fav_user = f.UserFactory() viewer_user = f.UserFactory() assigned_to_user = f.UserFactory() @@ -572,7 +588,7 @@ def test_get_voted_list_valid_info_for_not_project_types(): f.VotesFactory(content_type=content_type, object_id=instance.id, count=3) raw_instance_vote_info = get_voted_list(fav_user, viewer_user, type=object_type)[0] - instance_vote_info = VotedSerializer(raw_instance_vote_info).data + instance_vote_info = VotedObjectSerializer(raw_instance_vote_info).data assert instance_vote_info["type"] == object_type assert instance_vote_info["id"] == instance.id @@ -603,6 +619,87 @@ def test_get_voted_list_valid_info_for_not_project_types(): assert instance_vote_info["assigned_to_photo"] != "" +def test_get_watched_list_with_liked_and_voted_objects(client): + fav_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) + project.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(project) + f.LikeFactory(content_type=content_type, object_id=project.id, user=fav_user) + + voted_elements_factories = { + "userstory": f.UserStoryFactory, + "task": f.TaskFactory, + "issue": f.IssueFactory + } + + for object_type in voted_elements_factories: + instance = voted_elements_factories[object_type](project=project) + content_type = ContentType.objects.get_for_model(instance) + instance.add_watcher(fav_user) + f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) + + client.login(fav_user) + url = reverse('users-watched', kwargs={"pk": fav_user.pk}) + response = client.get(url, content_type="application/json") + + for element_data in response.data: + #assert element_data["is_watcher"] == True + if element_data["type"] == "project": + assert element_data["is_fan"] == True + else: + assert element_data["is_voter"] == True + + +def test_get_liked_list_with_watched_objects(client): + fav_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) + project.add_watcher(fav_user) + content_type = ContentType.objects.get_for_model(project) + f.LikeFactory(content_type=content_type, object_id=project.id, user=fav_user) + + client.login(fav_user) + url = reverse('users-liked', kwargs={"pk": fav_user.pk}) + response = client.get(url, content_type="application/json") + + element_data = response.data[0] + assert element_data["is_watcher"] == True + assert element_data["is_fan"] == True + + +def test_get_voted_list_with_watched_objects(client): + fav_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) + + voted_elements_factories = { + "userstory": f.UserStoryFactory, + "task": f.TaskFactory, + "issue": f.IssueFactory + } + + for object_type in voted_elements_factories: + instance = voted_elements_factories[object_type](project=project) + content_type = ContentType.objects.get_for_model(instance) + instance.add_watcher(fav_user) + f.VoteFactory(content_type=content_type, object_id=instance.id, user=fav_user) + + client.login(fav_user) + url = reverse('users-voted', kwargs={"pk": fav_user.pk}) + response = client.get(url, content_type="application/json") + + for element_data in response.data: + assert element_data["is_watcher"] == True + assert element_data["is_voter"] == True + + def test_get_watched_list_permissions(): fav_user = f.UserFactory() viewer_unpriviliged_user = f.UserFactory() @@ -637,6 +734,33 @@ def test_get_watched_list_permissions(): assert len(get_watched_list(fav_user, viewer_unpriviliged_user)) == 4 +def test_get_liked_list_permissions(): + fan_user = f.UserFactory() + viewer_unpriviliged_user = f.UserFactory() + viewer_priviliged_user = f.UserFactory() + + project = f.ProjectFactory(is_private=True, name="Testing project") + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=viewer_priviliged_user) + content_type = ContentType.objects.get_for_model(project) + f.LikeFactory(content_type=content_type, object_id=project.id, user=fan_user) + f.LikesFactory(content_type=content_type, object_id=project.id, count=1) + + #If the project is private a viewer user without any permission shouldn' see + # any vote + assert len(get_liked_list(fan_user, viewer_unpriviliged_user)) == 0 + + #If the project is private but the viewer user has permissions the votes should + # be accesible + assert len(get_liked_list(fan_user, viewer_priviliged_user)) == 1 + + #If the project is private but has the required anon permissions the votes should + # be accesible by any user too + project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] + project.save() + assert len(get_liked_list(fan_user, viewer_unpriviliged_user)) == 1 + + def test_get_voted_list_permissions(): fav_user = f.UserFactory() viewer_unpriviliged_user = f.UserFactory() @@ -645,9 +769,6 @@ def test_get_voted_list_permissions(): project = f.ProjectFactory(is_private=True, name="Testing project") role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) membership = f.MembershipFactory(project=project, role=role, user=viewer_priviliged_user) - content_type = ContentType.objects.get_for_model(project) - f.VoteFactory(content_type=content_type, object_id=project.id, user=fav_user) - f.VotesFactory(content_type=content_type, object_id=project.id, count=1) user_story = f.UserStoryFactory(project=project, subject="Testing user story") content_type = ContentType.objects.get_for_model(user_story) @@ -670,10 +791,10 @@ def test_get_voted_list_permissions(): #If the project is private but the viewer user has permissions the votes should # be accesible - assert len(get_voted_list(fav_user, viewer_priviliged_user)) == 4 + assert len(get_voted_list(fav_user, viewer_priviliged_user)) == 3 #If the project is private but has the required anon permissions the votes should # be accesible by any user too project.anon_permissions = ["view_project", "view_us", "view_tasks", "view_issues"] project.save() - assert len(get_voted_list(fav_user, viewer_unpriviliged_user)) == 4 + assert len(get_voted_list(fav_user, viewer_unpriviliged_user)) == 3 From c720f2252ff702dba5326b5fed79d1db5a6391d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Sat, 24 Oct 2015 18:22:14 +0200 Subject: [PATCH 170/190] Update requirements --- requirements-devel.txt | 6 +++--- requirements.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/requirements-devel.txt b/requirements-devel.txt index bca12fc2..cccd2a5e 100644 --- a/requirements-devel.txt +++ b/requirements-devel.txt @@ -1,13 +1,13 @@ -r requirements.txt -factory_boy==2.5.2 +factory_boy==2.6.0 py==1.4.30 pytest==2.8.2 pytest-django==2.9.1 pytest-pythonpath==0.7 -coverage==4.0 -coveralls==1.0 +coverage==4.0.1 +coveralls==1.1 django-slowdown==0.0.1 transifex-client==0.11.1.beta diff --git a/requirements.txt b/requirements.txt index 6d2df509..f5fd9762 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,13 +18,13 @@ django-sites==0.8 Markdown==2.6.2 fn==0.4.3 diff-match-patch==20121119 -requests==2.8.0 +requests==2.8.1 django-sr==0.0.4 easy-thumbnails==2.2 celery==3.1.18 redis==2.10.3 Unidecode==0.04.18 -raven==5.7.2 +raven==5.8.1 bleach==1.4.2 django-ipware==1.1.1 premailer==2.9.6 @@ -32,4 +32,4 @@ cssutils==1.0.1 # Compatible with python 3.5 django-transactional-cleanup==0.1.15 lxml==3.5.0b1 git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea -pyjwkest==1.0.5 +pyjwkest==1.0.6 From b988ae4bb3a2cb192bb35e4aa175e4537e720ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 26 Oct 2015 12:20:27 +0100 Subject: [PATCH 171/190] Sample data: Now project 2 and 4 are allways private --- taiga/projects/management/commands/sample_data.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index c9910962..1725db2e 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -123,7 +123,7 @@ class Command(BaseCommand): # create project for x in range(NUM_PROJECTS + NUM_EMPTY_PROJECTS): - project = self.create_project(x) + project = self.create_project(x, is_private=(x in [2, 4] or self.sd.boolean())) # added memberships computable_project_roles = set() @@ -437,8 +437,10 @@ class Command(BaseCommand): return milestone - def create_project(self, counter): - is_private=self.sd.boolean() + def create_project(self, counter, is_private=None): + if is_private is None: + is_private=self.sd.boolean() + anon_permissions = not is_private and list(map(lambda perm: perm[0], ANON_PERMISSIONS)) or [] public_permissions = not is_private and list(map(lambda perm: perm[0], ANON_PERMISSIONS)) or [] project = Project.objects.create(slug='project-%s'%(counter), From b0c57d8171d30fd00eabd4af09d0aed2dff45fe4 Mon Sep 17 00:00:00 2001 From: David Burke Date: Sat, 17 Oct 2015 15:53:28 -0400 Subject: [PATCH 172/190] Added ref GET param to resolver API. Useful if we know the the ref ID but not the type of object it is. Reduces number of API calls in cases when we want to reference an object by it's ref ID. Modified integration test to confirm ref works just like using a us, ect params and has same permissions. --- AUTHORS.rst | 1 + CHANGELOG.md | 1 + taiga/projects/references/api.py | 17 +++++++++ taiga/projects/references/serializers.py | 12 ++++++ .../test_resolver_resources.py | 18 +++++++++ .../integration/test_references_sequences.py | 38 +++++++++++++++++++ 6 files changed, 87 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index e7b493df..7242d6b1 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -20,6 +20,7 @@ answer newbie questions, and generally made taiga that much better: - Andrés Moya - Andrey Alekseenko - Chris Wilson +- David Burke - Hector Colina - Joe Letts - Julien Palard diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c92439..e8c423ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ - API: Mixin fields 'users', 'members' and 'memberships' in ProjectDetailSerializer. - API: Add stats/system resource with global server stats (total project, total users....) - API: Improve and fix some errors in issues/filters_data and userstories/filters_data. +- API: resolver suport ref GET param and return a story, task or issue. - Webhooks: Add deleted datetime to webhooks responses when isues, tasks or USs are deleted. - Add headers to allow threading for notification emails about changes to issues, tasks, user stories, and wiki pages. (thanks to [@brett](https://github.com/brettp)). - Lots of small and not so small bugfixes. diff --git a/taiga/projects/references/api.py b/taiga/projects/references/api.py index c515f6d1..4b8027cb 100644 --- a/taiga/projects/references/api.py +++ b/taiga/projects/references/api.py @@ -59,4 +59,21 @@ class ResolverViewSet(viewsets.ViewSet): result["wikipage"] = get_object_or_404(project.wiki_pages.all(), slug=data["wikipage"]).pk + if data["ref"]: + ref_found = False # No need to continue once one ref is found + if user_has_perm(request.user, "view_us", project): + us = project.user_stories.filter(ref=data["ref"]).first() + if us: + result["us"] = us.pk + ref_found = True + if ref_found is False and user_has_perm(request.user, "view_tasks", project): + task = project.tasks.filter(ref=data["ref"]).first() + if task: + result["task"] = task.pk + ref_found = True + if ref_found is False and user_has_perm(request.user, "view_issues", project): + issue = project.issues.filter(ref=data["ref"]).first() + if issue: + result["issue"] = issue.pk + return response.Ok(result) diff --git a/taiga/projects/references/serializers.py b/taiga/projects/references/serializers.py index ad9b5def..4755a897 100644 --- a/taiga/projects/references/serializers.py +++ b/taiga/projects/references/serializers.py @@ -23,4 +23,16 @@ class ResolverSerializer(serializers.Serializer): us = serializers.IntegerField(required=False) task = serializers.IntegerField(required=False) issue = serializers.IntegerField(required=False) + ref = serializers.IntegerField(required=False) wikipage = serializers.CharField(max_length=512, required=False) + + def validate(self, attrs): + if "ref" in attrs: + if "us" in attrs: + raise serializers.ValidationError("'us' param is incompatible with 'ref' in the same request") + if "task" in attrs: + raise serializers.ValidationError("'task' param is incompatible with 'ref' in the same request") + if "issue" in attrs: + raise serializers.ValidationError("'issue' param is incompatible with 'ref' in the same request") + + return attrs diff --git a/tests/integration/resources_permissions/test_resolver_resources.py b/tests/integration/resources_permissions/test_resolver_resources.py index 0cb484a3..4f5f4f54 100644 --- a/tests/integration/resources_permissions/test_resolver_resources.py +++ b/tests/integration/resources_permissions/test_resolver_resources.py @@ -128,3 +128,21 @@ def test_resolver_list(client, data): "task": data.task.pk, "issue": data.issue.pk, "milestone": data.milestone.pk} + + response = client.json.get("{}?project={}&ref={}".format(url, + data.private_project2.slug, + data.us.ref)) + assert response.data == {"project": data.private_project2.pk, + "us": data.us.pk} + + response = client.json.get("{}?project={}&ref={}".format(url, + data.private_project2.slug, + data.task.ref)) + assert response.data == {"project": data.private_project2.pk, + "task": data.task.pk} + + response = client.json.get("{}?project={}&ref={}".format(url, + data.private_project2.slug, + data.issue.ref)) + assert response.data == {"project": data.private_project2.pk, + "issue": data.issue.pk} diff --git a/tests/integration/test_references_sequences.py b/tests/integration/test_references_sequences.py index 0ec9d35f..815bf420 100644 --- a/tests/integration/test_references_sequences.py +++ b/tests/integration/test_references_sequences.py @@ -17,6 +17,8 @@ import pytest +from django.core.urlresolvers import reverse + from .. import factories @@ -141,3 +143,39 @@ def test_regenerate_issue_reference_on_project_change(seq, refmodels): issue.save() assert issue.ref == 201 + + +@pytest.mark.django_db +def test_params_validation_in_api_request(client, refmodels): + user = factories.UserFactory.create() + project = factories.ProjectFactory.create(owner=user) + seqname1 = refmodels.make_sequence_name(project) + role = factories.RoleFactory.create(project=project) + factories.MembershipFactory.create(project=project, user=user, role=role, is_owner=True) + + milestone = factories.MilestoneFactory.create(project=project) + us = factories.UserStoryFactory.create(project=project) + task = factories.TaskFactory.create(project=project) + issue = factories.IssueFactory.create(project=project) + wiki_page = factories.WikiPageFactory.create(project=project) + + client.login(user) + + url = reverse("resolver-list") + response = client.json.get(url) + assert response.status_code == 400 + response = client.json.get("{}?project={}".format(url, project.slug)) + assert response.status_code == 200 + response = client.json.get("{}?project={}&ref={}".format(url, project.slug, us.ref)) + assert response.status_code == 200 + response = client.json.get("{}?project={}&ref={}&us={}".format(url, project.slug, us.ref, us.ref)) + assert response.status_code == 400 + response = client.json.get("{}?project={}&ref={}&task={}".format(url, project.slug, us.ref, task.ref)) + assert response.status_code == 400 + response = client.json.get("{}?project={}&ref={}&issue={}".format(url, project.slug, us.ref, issue.ref)) + assert response.status_code == 400 + response = client.json.get("{}?project={}&us={}&task={}".format(url, project.slug, us.ref, task.ref)) + assert response.status_code == 200 + response = client.json.get("{}?project={}&ref={}&milestone={}".format(url, project.slug, us.ref, + milestone.slug)) + assert response.status_code == 200 From e98b4b9e1946e2496ff50c3930cf2ed9ce4f4a05 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 26 Oct 2015 10:03:30 +0100 Subject: [PATCH 173/190] Issue 3372: Velocity not calculated properly --- taiga/projects/services/stats.py | 6 ++++- tests/integration/test_stats.py | 40 +++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index 998e5442..70d4cc3f 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -224,7 +224,11 @@ def get_stats_for_project(project): get(id=project.id) points = project.calculated_points - closed_points = sum(points["closed"].values()) + + closed_milestone_query = Q(role_points__user_story__milestone__closed=True) + null_milestone_query = Q(role_points__user_story__milestone__isnull=True) + closed_points = sum(project.points.filter(closed_milestone_query|null_milestone_query)\ + .exclude(value__isnull=True).values_list("value", flat=True)) closed_milestones = project.milestones.filter(closed=True).count() speed = 0 if closed_milestones != 0: diff --git a/tests/integration/test_stats.py b/tests/integration/test_stats.py index 4dfab9e4..374985c3 100644 --- a/tests/integration/test_stats.py +++ b/tests/integration/test_stats.py @@ -3,6 +3,9 @@ import pytest from .. import factories as f from tests.utils import disconnect_signals, reconnect_signals +from taiga.projects.services.stats import get_stats_for_project + + pytestmark = pytest.mark.django_db @@ -30,6 +33,8 @@ def data(): m.points2 = f.PointsFactory(project=m.project, value=2) m.points3 = f.PointsFactory(project=m.project, value=4) m.points4 = f.PointsFactory(project=m.project, value=8) + m.points5 = f.PointsFactory(project=m.project, value=16) + m.points6 = f.PointsFactory(project=m.project, value=32) m.open_status = f.UserStoryStatusFactory(is_closed=False) m.closed_status = f.UserStoryStatusFactory(is_closed=True) @@ -54,11 +59,23 @@ def data(): user_story__project=m.project, user_story__status=m.open_status, user_story__milestone=None) + # 5 and 6 are in the same milestone + m.role_points5 = f.RolePointsFactory(role=m.project.roles.all()[0], + points=m.points5, + user_story__project=m.project, + user_story__status=m.open_status) + m.role_points6 = f.RolePointsFactory(role=m.project.roles.all()[0], + points=m.points6, + user_story__project=m.project, + user_story__status=m.open_status, + user_story__milestone=m.role_points5.user_story.milestone) m.user_story1 = m.role_points1.user_story m.user_story2 = m.role_points2.user_story m.user_story3 = m.role_points3.user_story m.user_story4 = m.role_points4.user_story + m.user_story5 = m.role_points5.user_story + m.user_story6 = m.role_points6.user_story m.milestone = f.MilestoneFactory(project=m.project) @@ -66,10 +83,10 @@ def data(): def test_project_defined_points(client, data): - assert data.project.defined_points == {data.role1.pk: 15} + assert data.project.defined_points == {data.role1.pk: 63} data.role_points1.role = data.role2 data.role_points1.save() - assert data.project.defined_points == {data.role1.pk: 14, data.role2.pk: 1} + assert data.project.defined_points == {data.role1.pk: 62, data.role2.pk: 1} def test_project_closed_points(client, data): @@ -89,22 +106,29 @@ def test_project_closed_points(client, data): data.user_story4.is_closed = True data.user_story4.save() assert data.project.closed_points == {data.role1.pk: 14, data.role2.pk: 1} + #User story5 milestone isn't closed + data.user_story5.is_closed = True + data.user_story5.save() + assert data.project.closed_points == {data.role1.pk: 30, data.role2.pk: 1} + + project_stats = get_stats_for_project(data.project) + assert project_stats["closed_points"] == 15 def test_project_assigned_points(client, data): - assert data.project.assigned_points == {} + assert data.project.assigned_points == {data.role1.pk: 48} data.role_points1.role = data.role2 data.role_points1.save() - assert data.project.assigned_points == {} + assert data.project.assigned_points == {data.role1.pk: 48} data.user_story1.milestone = data.milestone data.user_story1.save() - assert data.project.assigned_points == {data.role2.pk: 1} + assert data.project.assigned_points == {data.role1.pk: 48, data.role2.pk: 1} data.user_story2.milestone = data.milestone data.user_story2.save() - assert data.project.assigned_points == {data.role1.pk: 2, data.role2.pk: 1} + assert data.project.assigned_points == {data.role1.pk: 50, data.role2.pk: 1} data.user_story3.milestone = data.milestone data.user_story3.save() - assert data.project.assigned_points == {data.role1.pk: 6, data.role2.pk: 1} + assert data.project.assigned_points == {data.role1.pk: 54, data.role2.pk: 1} data.user_story4.milestone = data.milestone data.user_story4.save() - assert data.project.assigned_points == {data.role1.pk: 14, data.role2.pk: 1} + assert data.project.assigned_points == {data.role1.pk: 62, data.role2.pk: 1} From c23a21a9d639162e14028d56fa3b0d0528c07330 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 27 Oct 2015 08:07:39 +0100 Subject: [PATCH 174/190] Fixing bug in watched API call related to ignored notification levels --- taiga/users/services.py | 4 +++- tests/integration/test_users.py | 18 ++++++++++++++++++ tests/integration/test_watch_projects.py | 2 +- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/taiga/users/services.py b/taiga/users/services.py index 4542d22b..aea50ca4 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -224,7 +224,9 @@ def _build_watched_sql_for_projects(for_user): ON projects_project.id = type_watchers.project_id LEFT JOIN likes_likes ON (projects_project.id = likes_likes.object_id AND {project_content_type_id} = likes_likes.content_type_id) - WHERE notifications_notifypolicy.user_id = {for_user_id} + WHERE + notifications_notifypolicy.user_id = {for_user_id} + AND notifications_notifypolicy.notify_level != {ignore_notify_level} """ sql = sql.format( for_user_id=for_user.id, diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index 1a5959e0..d9990d45 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -13,6 +13,8 @@ from taiga.users.serializers import LikedObjectSerializer, VotedObjectSerializer from taiga.auth.tokens import get_token_for_user from taiga.permissions.permissions import MEMBERS_PERMISSIONS, ANON_PERMISSIONS, USER_PERMISSIONS from taiga.users.services import get_watched_list, get_voted_list, get_liked_list +from taiga.projects.notifications.choices import NotifyLevel +from taiga.projects.notifications.models import NotifyPolicy from easy_thumbnails.files import generate_all_aliases, get_thumbnailer @@ -470,6 +472,22 @@ def test_get_watched_list_valid_info_for_project(): assert project_watch_info["assigned_to_photo"] == None +def test_get_watched_list_for_project_with_ignored_notify_level(): + #If the notify policy level is ignore the project shouldn't be in the watched results + fav_user = f.UserFactory() + viewer_user = f.UserFactory() + + project = f.ProjectFactory(is_private=False, name="Testing project", tags=['test', 'tag']) + role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) + membership = f.MembershipFactory(project=project, role=role, user=fav_user) + notify_policy = NotifyPolicy.objects.get(user=fav_user, project=project) + notify_policy.notify_level=NotifyLevel.ignore + notify_policy.save() + + watched_list = get_watched_list(fav_user, viewer_user) + assert len(watched_list) == 0 + + def test_get_liked_list_valid_info(): fan_user = f.UserFactory() viewer_user = f.UserFactory() diff --git a/tests/integration/test_watch_projects.py b/tests/integration/test_watch_projects.py index 724c5106..fd1c9560 100644 --- a/tests/integration/test_watch_projects.py +++ b/tests/integration/test_watch_projects.py @@ -69,7 +69,7 @@ def test_watch_project_with_invalid_notify_level(client): assert response.data["_error_message"] == "Invalid value for notify level" -def test_unwacth_project(client): +def test_unwatch_project(client): user = f.UserFactory.create() project = f.create_project(owner=user) f.MembershipFactory.create(project=project, user=user, is_owner=True) From 02bd761944e538e8c5331f42a94166abedd42c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 27 Oct 2015 09:54:40 +0100 Subject: [PATCH 175/190] Make script manage_translations.py compatible with django 1.8 --- scripts/manage_translations.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/manage_translations.py b/scripts/manage_translations.py index 22bcccbc..c7a2d1ff 100644 --- a/scripts/manage_translations.py +++ b/scripts/manage_translations.py @@ -84,6 +84,7 @@ def update_catalogs(resources=None, languages=None): cmd = makemessages.Command() opts = { "locale": ["en"], + "exclude": [], "extensions": ["py", "jinja"], # Default values @@ -96,7 +97,7 @@ def update_catalogs(resources=None, languages=None): "no_location": False, "no_obsolete": False, "keep_pot": False, - "verbosity": "0", + "verbosity": 0, } if resources is not None: From b3daa319f5b0a180fea1bd68a4b6a4efb7aff97c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Tue, 27 Oct 2015 09:55:00 +0100 Subject: [PATCH 176/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/de/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/en/LC_MESSAGES/django.po | 228 +++++++------ taiga/locale/es/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/fi/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/fr/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/it/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/nl/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/pl/LC_MESSAGES/django.po | 190 ++++++----- taiga/locale/pt_BR/LC_MESSAGES/django.po | 232 ++++++------- taiga/locale/ru/LC_MESSAGES/django.po | 360 +++++++++++---------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 232 ++++++------- 12 files changed, 1518 insertions(+), 1348 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 8422123a..4e60447a 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" "MIME-Version: 1.0\n" @@ -41,27 +41,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Requerit. 255 caràcters o menys. Lletres, nombres i caràcters /./-/_" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "El mot d'usuari ja està en ús." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Aquest e-mail ja està en ús." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "El token no s'ajusta a cap invitació vàlida" -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Aquest usuari ja està registrat" -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Aquest usuari ja es membre" -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Error creant un nou usuari." @@ -537,17 +537,17 @@ msgid "It contain invalid custom fields." msgstr "Conté camps personalitzats invàlids." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "" @@ -704,7 +704,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -723,8 +723,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -739,8 +739,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -762,11 +762,12 @@ msgstr "Comentari" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "Data de creació" @@ -844,26 +845,26 @@ msgstr "El projecte no existeix" msgid "Bad signature" msgstr "Firma no vàlida." -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'element referenciat no existeix" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'estatus no existeix." -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Informació d'incidència no vàlida." -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -874,17 +875,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Informació del comentari a l'incidència no vàlid." -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -895,7 +896,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1134,19 +1135,19 @@ msgstr "Administrar valors de projecte" msgid "Admin roles" msgstr "Administrar rols" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Al menys un del usuaris ha de ser administrador" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "No tens permisos per a veure açò." @@ -1161,7 +1162,7 @@ msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "Amo" @@ -1175,8 +1176,8 @@ msgstr "Amo" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "Projecte" @@ -1193,7 +1194,7 @@ msgstr "Id d'objecte" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "Data de modificació" @@ -1215,7 +1216,7 @@ msgstr "està obsolet " #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "Ordre" @@ -1235,14 +1236,18 @@ msgstr "" msgid "Talky" msgstr "" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1388,17 +1393,17 @@ msgstr "Desde:" msgid "To:" msgstr "A:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "contingut" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "nota de bloqueig" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "" @@ -1464,10 +1469,23 @@ msgstr "assignada a" msgid "external reference" msgstr "referència externa" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1714,12 +1732,12 @@ msgstr "" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2224,47 +2242,47 @@ msgstr "Versió" msgid "You can't leave the project if there are no more owners" msgstr "No pots deixar el projecte si no hi ha més amos" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Aquest e-mail ja està en ús" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rol invàlid per al projecte" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Opcions per defecte" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Estatus d'històries d'usuari" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Punts" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Estatus de tasques" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Estatus d'incidéncies" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Tipus d'incidéncies" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Prioritats" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Severitats" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Rols" @@ -2724,10 +2742,6 @@ msgstr "No hi ha cap estatis d'història d'usuari amb eixe id" msgid "There's no task status with that id" msgstr "No hi ha cap estatus de tasca amb eixe id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -2745,11 +2759,11 @@ msgstr "" msgid "'project_id' parameter is mandatory" msgstr "" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "últim a modificar" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -2769,57 +2783,57 @@ msgstr "Permissos" msgid "Important dates" msgstr "Dates importants" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Nom d'usuari o email invàlid" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Correu enviat satisfactòriament" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Token invàlid" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Paràmetre de password actual requerit" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Paràmetre de password requerit" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Password invàlid, al menys 6 caràcters requerits" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Password actual invàlid" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Arguments incomplets." - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Format d'image invàlid" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Email duplicat" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Email no vàlid" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Nom d'usuari o email invàlid" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Correu enviat satisfactòriament" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Token invàlid" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Paràmetre de password actual requerit" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Paràmetre de password requerit" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Password invàlid, al menys 6 caràcters requerits" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Password actual invàlid" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Arguments incomplets." + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Format d'image invàlid" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invàlid. Estás segur que el token es correcte i que no l'has usat abans?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Invàlid. Estás segur que el token es correcte?" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index a7fd30ca..c2ee143b 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -15,9 +15,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -49,27 +49,27 @@ msgstr "" "255 oder weniger Zeichen aus Buchstaben, Zahlen und Punkt, Minus oder " "Unterstrich erforderlich." -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Der Benutzername wird schon verwendet." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Diese E-Mail Adresse wird schon verwendet." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Das Token kann keiner gültigen Einladung zugeordnet werden." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Der Benutzer ist schon registriert." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Der Benutzer für diese Mitgliedschaft existiert bereits." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Fehler bei der Erstellung des neuen Benutzers." @@ -590,17 +590,17 @@ msgid "It contain invalid custom fields." msgstr "Enthält ungültige Benutzerfelder." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Der Name für das Projekt ist doppelt vergeben" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Fehler beim Erzeugen der Projekt Export-Datei " -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Fehler beim Laden von Projekt Export-Datei" @@ -850,7 +850,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -869,8 +869,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -885,8 +885,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -908,11 +908,12 @@ msgstr "Kommentar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "Erstellungsdatum" @@ -990,26 +991,26 @@ msgstr "Das Projekt existiert nicht" msgid "Bad signature" msgstr "Falsche Signatur" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Das referenzierte Element existiert nicht" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Der Status existiert nicht" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Der Status des BitBucket Commits hat sich geändert" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Ungültige Ticket-Information" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1020,17 +1021,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "Ticket erstellt von BitBucket." -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Ungültige Ticket-Kommentar Information" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1041,7 +1042,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1304,19 +1305,19 @@ msgstr "Administrator Projekt Werte" msgid "Admin roles" msgstr "Administrator-Rollen" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Unglültiger Templatename" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Ungültige Templatebeschreibung" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Mindestens ein Benutzer muss ein aktiver Administrator sein. " -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Sie haben keine Berechtigungen für diese Ansicht" @@ -1331,7 +1332,7 @@ msgstr "Nr. unterschreidet sich zwischen dem Objekt und dem Projekt" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "Besitzer" @@ -1345,8 +1346,8 @@ msgstr "Besitzer" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "Projekt" @@ -1363,7 +1364,7 @@ msgstr "Objekt Nr." #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "Zeitpunkt der Änderung" @@ -1385,7 +1386,7 @@ msgstr "wurde verworfen" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "Reihenfolge" @@ -1405,14 +1406,18 @@ msgstr "Kunde" msgid "Talky" msgstr "Gesprächig" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Text" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Mehrzeiliger Text" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1558,17 +1563,17 @@ msgstr "Von:" msgid "To:" msgstr "An:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "Inhalt" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "Blockierungsgrund" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "Sprint" @@ -1638,10 +1643,23 @@ msgstr "zugewiesen an" msgid "external reference" msgstr "externe Referenz" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "Slug" @@ -1888,12 +1906,12 @@ msgstr "Benutzer benachrichtigen" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2678,47 +2696,47 @@ msgstr "" "Sie können das Projekt nicht verlassen, wenn keine weiteren Besitzer " "vorhanden sind" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Die E-Mailadresse ist bereits vergeben" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Ungültige Rolle für dieses Projekt" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Voreingestellte Optionen" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Status für User-Stories" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Punkte" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Aufgaben Status" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Ticket Status" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Ticket Arten" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Prioritäten" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Gewichtung" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Rollen" @@ -3203,10 +3221,6 @@ msgstr "Es gibt keinen User-Story Status mit dieser id" msgid "There's no task status with that id" msgstr "Es gibt keinen Aufgabenstatus mit dieser id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3224,11 +3238,11 @@ msgstr "'content' Parameter ist erforderlich" msgid "'project_id' parameter is mandatory" msgstr "'project_id' Parameter ist erforderlich" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "letzte Änderung" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3248,58 +3262,58 @@ msgstr "Berechtigungen" msgid "Important dates" msgstr "Wichtige Termine" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Ungültiger Benutzername oder E-Mail" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "E-Mail erfolgreich gesendet." - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Token ist ungültig" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Aktueller Passwort Parameter wird benötigt" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Neuer Passwort Parameter benötigt" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Ungültige Passwortlänge, mindestens 6 Zeichen erforderlich" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Ungültiges aktuelles Passwort" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Unvollständige Argumente" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Ungültiges Bildformat" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Doppelte E-Mail" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Ungültige E-Mail" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Ungültiger Benutzername oder E-Mail" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "E-Mail erfolgreich gesendet." + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Token ist ungültig" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Aktueller Passwort Parameter wird benötigt" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Neuer Passwort Parameter benötigt" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Ungültige Passwortlänge, mindestens 6 Zeichen erforderlich" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Ungültiges aktuelles Passwort" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Unvollständige Argumente" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Ungültiges Bildformat" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Ungültig. Sind Sie sicher, dass das Token korrekt ist und Sie es nicht " "bereits verwendet haben?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Ungültig. Sind Sie sicher, dass das Token korrekt ist?" diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index f3f60e36..192c9781 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -37,27 +37,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "" -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "" -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "" -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "" -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "" -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "" @@ -526,17 +526,17 @@ msgid "It contain invalid custom fields." msgstr "" #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "" @@ -693,7 +693,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -712,8 +712,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -728,8 +728,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -751,11 +751,12 @@ msgstr "" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "" @@ -817,26 +818,26 @@ msgstr "" msgid "Bad signature" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -847,17 +848,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -868,7 +869,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1107,19 +1108,19 @@ msgstr "" msgid "Admin roles" msgstr "" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "" @@ -1134,7 +1135,7 @@ msgstr "" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "" @@ -1148,8 +1149,8 @@ msgstr "" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "" @@ -1166,7 +1167,7 @@ msgstr "" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "" @@ -1188,7 +1189,7 @@ msgstr "" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "" @@ -1208,14 +1209,18 @@ msgstr "" msgid "Talky" msgstr "" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1361,17 +1366,17 @@ msgstr "" msgid "To:" msgstr "" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "" @@ -1437,10 +1442,23 @@ msgstr "" msgid "external reference" msgstr "" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "" @@ -1687,12 +1705,12 @@ msgstr "" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2191,47 +2209,47 @@ msgstr "" msgid "You can't leave the project if there are no more owners" msgstr "" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "" @@ -2671,10 +2689,6 @@ msgstr "" msgid "There's no task status with that id" msgstr "" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -2692,11 +2706,11 @@ msgstr "" msgid "'project_id' parameter is mandatory" msgstr "" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "" @@ -2716,56 +2730,56 @@ msgstr "" msgid "Important dates" msgstr "" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index d8536c66..8c3e81ba 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:40+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" @@ -44,27 +44,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Son necesarios. 255 caracteres o menos (letras, números y /./-/_)" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Nombre de usuario no disponible" -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Email no disponible" -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "El token no pertenece a ninguna invitación válida." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Este usuario ya está registrado." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Ya existe una membresía asociada a este usuario." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Error al crear un nuevo usuario " @@ -575,17 +575,17 @@ msgid "It contain invalid custom fields." msgstr "Contiene attributos personalizados inválidos." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nombre duplicado para el proyecto" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Erro generando el volcado de datos del proyecto" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Error cargando el volcado de datos del proyecto" @@ -830,7 +830,7 @@ msgid "Authentication required" msgstr "Se requiere autenticación" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -849,8 +849,8 @@ msgid "web" msgstr "web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -865,8 +865,8 @@ msgstr "Siguiente URL" msgid "secret key for ciphering the application tokens" msgstr "clave secreta para cifrar los tokens de aplicación" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "usuario" @@ -888,11 +888,12 @@ msgstr "comentario" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "fecha de creación" @@ -969,26 +970,26 @@ msgstr "El proyecto no existe" msgid "Bad signature" msgstr "Firma errónea" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "El elemento referenciado no existe" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "El estado no existe" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Estado cambiado desde un commit de BitBucket" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Información inválida de Issue" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1005,17 +1006,17 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "Petición creada desde BitBucket." -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Información de comentario de Issue inválida" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1032,7 +1033,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1300,19 +1301,19 @@ msgstr "Administrar valores de proyecto" msgid "Admin roles" msgstr "Administrar roles" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Nombre de plantilla invalido" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Descripción de plantilla invalida" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Al menos uno de los usuario debe ser un administrador." -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "No tienes suficientes permisos para ver esto." @@ -1327,7 +1328,7 @@ msgstr "El ID de proyecto no coincide entre el adjunto y un proyecto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "Dueño" @@ -1341,8 +1342,8 @@ msgstr "Dueño" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "Proyecto" @@ -1359,7 +1360,7 @@ msgstr "id de objeto" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "fecha modificada" @@ -1381,7 +1382,7 @@ msgstr "está desactualizado" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "orden" @@ -1401,14 +1402,18 @@ msgstr "Personalizado" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Texto" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Texto multilínea" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1554,17 +1559,17 @@ msgstr "De:" msgid "To:" msgstr "A:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "contenido" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "nota de bloqueo" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "sprint" @@ -1630,10 +1635,23 @@ msgstr "asignado a" msgid "external reference" msgstr "referencia externa" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "recuento" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1882,13 +1900,13 @@ msgstr "usuarios notificados" msgid "Watched" msgstr "Observado" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "" "Ya existe una política de notificación para este usuario en el proyecto." -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "Valor inválido para el nivel de notificación" @@ -2624,47 +2642,47 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "No puedes abandonar este proyecto si no existen mas propietarios del mismo" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "La dirección de email ya está en uso." -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rol inválido para el proyecto" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Opciones por defecto" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Estados de historia de usuario" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Puntos" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Estado de tareas" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Estados de peticion" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Tipos de petición" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Gravedades" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Roles" @@ -3160,10 +3178,6 @@ msgstr "No existe ningún estado de historia con este id" msgid "There's no task status with that id" msgstr "No existe ningún estado de tarea con este id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "recuento" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3181,11 +3195,11 @@ msgstr "el parámetro 'content' es obligatório" msgid "'project_id' parameter is mandatory" msgstr "el parámetro 'project_id' es obligatório" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "última modificación por" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3205,57 +3219,57 @@ msgstr "Permisos" msgid "Important dates" msgstr "datos importántes" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Nombre de usuario o email no válidos" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "¡Correo enviado con éxito!" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "token inválido" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "La contraseña actual es obligatoria." - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "La nueva contraseña es obligatoria" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "La longitud de la contraseña debe de ser de al menos 6 caracteres" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Contraseña actual inválida" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Argumentos incompletos" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Formato de imagen no válido" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Email duplicado" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Email no válido" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Nombre de usuario o email no válidos" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "¡Correo enviado con éxito!" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "token inválido" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "La contraseña actual es obligatoria." + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "La nueva contraseña es obligatoria" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "La longitud de la contraseña debe de ser de al menos 6 caracteres" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Contraseña actual inválida" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Argumentos incompletos" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Formato de imagen no válido" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invalido, ¿estás seguro de que el token es correcto y no se ha usado antes?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, ¿estás seguro de que el token es correcto?" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 13df40c5..1323f434 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" "MIME-Version: 1.0\n" @@ -42,27 +42,27 @@ msgid "" msgstr "" "Vaaditaan. Korkeintaan 255 merkkiä. Kirjaimia, numeroita /./-/_ merkkejä'" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Käyttäjänimi on varattu." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Sähköposti on jo varattu." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Tunniste ei vastaa mihinkään avoimeen kutsuun." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Käyttäjä on jo rekisteröitynyt." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Jäsenyys on jo olemassa." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Virhe käyttäjän luonnissa." @@ -562,17 +562,17 @@ msgid "It contain invalid custom fields." msgstr "Sisältää vieheellisiä omia kenttiä." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nimi on tuplana projektille" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Virhe tiedoston luonnissa" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Virhe tiedoston latauksessa" @@ -814,7 +814,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -833,8 +833,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -849,8 +849,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -872,11 +872,12 @@ msgstr "kommentti" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "luontipvm" @@ -955,26 +956,26 @@ msgstr "Projektia ei löydy" msgid "Bad signature" msgstr "Virheellinen allekirjoitus" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Viitattu elementtiä ei löydy" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Tilaa ei löydy" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Tila muutettu BitBucket kommitilla" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Virheellinen pyynnön tieto" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -985,17 +986,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Virheellinen pyynnön kommentin tieto" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1006,7 +1007,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1263,19 +1264,19 @@ msgstr "Hallinnoi projektin arvoja" msgid "Admin roles" msgstr "Hallinnoi rooleja" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Virheellinen mallipohjan nimi" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Virheellinen mallipohjan kuvaus" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Vähintään yhden käyttäjän pitää olla aktiivinen ylläpitäjä" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Sinulla ei ole oikeuksia nähdä tätä." @@ -1290,7 +1291,7 @@ msgstr "Projekti ID ei vastaa kohdetta ja projektia" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "omistaja" @@ -1304,8 +1305,8 @@ msgstr "omistaja" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "projekti" @@ -1322,7 +1323,7 @@ msgstr "objekti ID" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "muokkauspvm" @@ -1344,7 +1345,7 @@ msgstr "on poistettu" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "order" @@ -1364,14 +1365,18 @@ msgstr "" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1517,17 +1522,17 @@ msgstr "Keneltä:" msgid "To:" msgstr "Kenelle:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "sisältö" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "suljettu muistiinpano" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "" @@ -1593,10 +1598,23 @@ msgstr "tekijä" msgid "external reference" msgstr "ulkoinen viittaus" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "hukka-aika" @@ -1843,12 +1861,12 @@ msgstr "ilmoita käyttäjille" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "Ilmoita olemassaolosta määritellyille käyttäjille ja projektille" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2593,47 +2611,47 @@ msgstr "versio" msgid "You can't leave the project if there are no more owners" msgstr "Et voi jättää projektia, jos olet ainoa omistaja" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Sähköpostiosoite on jo käytössä" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Virheellinen rooli projektille" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Oletusoptiot" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Käyttäjätarinatilat" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Pisteet" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Tehtävien tilat" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Pyyntöjen tilat" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "pyyntötyypit" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Kiireellisyydet" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Vakavuudet" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Roolit" @@ -3124,10 +3142,6 @@ msgstr "En löydä käyttäjätarinan tilaa tällä id:llä" msgid "There's no task status with that id" msgstr "En löydä tehtävän tilaa tällä id:llä" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3145,11 +3159,11 @@ msgstr "'content' parametri on pakollinen" msgid "'project_id' parameter is mandatory" msgstr "'project_id' parametri on pakollinen" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "viimeksi muokannut" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3169,58 +3183,58 @@ msgstr "Oikeudet" msgid "Important dates" msgstr "Tärkeät päivämäärät" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Tuntematon käyttäjänimi tai sähköposti" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Sähköposti lähetetty." - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Tunniste on virheellinen" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Nykyinen salasanaparametri tarvitaan" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Uusi salasanaparametri tarvitaan" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Salasanan pitää olla vähintään 6 merkkiä pitkä" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Virheellinen nykyinen salasana" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Puutteelliset argumentit" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Väärä kuvaformaatti" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Sähköposti on jo olemassa" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Virheellinen sähköposti" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Tuntematon käyttäjänimi tai sähköposti" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Sähköposti lähetetty." + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Tunniste on virheellinen" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Nykyinen salasanaparametri tarvitaan" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Uusi salasanaparametri tarvitaan" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Salasanan pitää olla vähintään 6 merkkiä pitkä" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Virheellinen nykyinen salasana" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Puutteelliset argumentit" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Väärä kuvaformaatti" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Virheellinen. Oletko varma, että tunniste on oikea ja et ole jo käyttänyt " "sitä?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Virheellinen, oletko varma että tunniste on oikea?" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 2f77501a..90130ef3 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -17,9 +17,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" "MIME-Version: 1.0\n" @@ -50,27 +50,27 @@ msgid "" msgstr "" "Requis. 255 caractères ou moins. Lettres, chiffres et caractères /./-/_'" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Ce nom d'utilisateur est déjà utilisé." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Cette adresse email est déjà utilisée." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Le jeton ne correspond à aucune invitation." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Cet utilisateur est déjà inscrit." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Cet utilisateur est déjà membre." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Erreur à la création du nouvel utilisateur." @@ -592,17 +592,17 @@ msgid "It contain invalid custom fields." msgstr "Contient des champs personnalisés non valides." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nom dupliqué pour ce projet" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Error dans la génération du dump du projet" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Erreur au chargement du dump du projet" @@ -803,7 +803,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -822,8 +822,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -838,8 +838,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "utilisateur" @@ -861,11 +861,12 @@ msgstr "Commentaire" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "Date de création" @@ -942,26 +943,26 @@ msgstr "Le projet n'existe pas" msgid "Bad signature" msgstr "Signature non valide" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'élément référencé n'existe pas" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "L'état n'existe pas" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Statut changé depuis un commit BitBucket" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Information incorrecte sur le problème" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -972,17 +973,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Ignoré" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -993,7 +994,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1235,19 +1236,19 @@ msgstr "Administrer les paramètres du projet" msgid "Admin roles" msgstr "Administrer les rôles" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Nom de modèle non valide" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Description du modèle non valide" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Au moins un utilisateur doit être un administrateur actif" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Vous n'avez pas les permissions pour consulter cet élément" @@ -1262,7 +1263,7 @@ msgstr "L'identifiant du projet de correspond pas entre l'objet et le projet" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "propriétaire" @@ -1276,8 +1277,8 @@ msgstr "propriétaire" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "projet" @@ -1294,7 +1295,7 @@ msgstr "identifiant de l'objet" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "état modifié" @@ -1316,7 +1317,7 @@ msgstr "est obsolète" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "ordre" @@ -1336,14 +1337,18 @@ msgstr "" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Texte" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Texte multi-ligne" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1489,17 +1494,17 @@ msgstr "De :" msgid "To:" msgstr "A :" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "contenu" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "note bloquée" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "sprint" @@ -1565,10 +1570,23 @@ msgstr "assigné à" msgid "external reference" msgstr "référence externe" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1815,12 +1833,12 @@ msgstr "notifier les utilisateurs" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "La notification existe pour l'utilisateur et le projet spécifiés" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2338,47 +2356,47 @@ msgid "You can't leave the project if there are no more owners" msgstr "" "Vous ne pouvez pas quitter le projet si il n'y a plus d'autres propriétaires" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Adresse email déjà existante" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Rôle non valide pour le projet" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Options par défaut" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Etats de la User Story" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Points" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Etats des tâches" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Statuts des problèmes" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Types de problèmes" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Priorités" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Sévérités" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Rôles" @@ -2857,10 +2875,6 @@ msgstr "Il n'y a pas de statut d'user story avec cet id" msgid "There's no task status with that id" msgstr "Il n'y a pas de statut de tâche avec cet id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -2878,11 +2892,11 @@ msgstr "'content' paramètre obligatoire" msgid "'project_id' parameter is mandatory" msgstr "'project_id' paramètre obligatoire" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "dernier modificateur" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -2902,58 +2916,58 @@ msgstr "Permissions" msgid "Important dates" msgstr "Dates importantes" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Nom d'utilisateur ou email non valide" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Mail envoyé avec succès!" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Jeton invalide" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Paramètre 'mot de passe actuel' requis" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Paramètre 'nouveau mot de passe' requis" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Le mot de passe doit être d'au moins 6 caractères" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Mot de passe actuel incorrect" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "arguments manquants" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "format de l'image non valide" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Email dupliquée" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Email non valide" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Nom d'utilisateur ou email non valide" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Mail envoyé avec succès!" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Jeton invalide" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Paramètre 'mot de passe actuel' requis" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Paramètre 'nouveau mot de passe' requis" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Le mot de passe doit être d'au moins 6 caractères" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Mot de passe actuel incorrect" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "arguments manquants" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "format de l'image non valide" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Invalide, êtes-vous sûre que le jeton est correct et qu'il n'a pas déjà été " "utilisé ?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Invalide, êtes-vous sûre que le jeton est correct ?" diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index 51da3973..5ce9ea7b 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -13,9 +13,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:40+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/it/)\n" "MIME-Version: 1.0\n" @@ -47,27 +47,27 @@ msgstr "" "Sono richiesti 255 caratteri, o meno, contenenti: lettere, numeri e " "caratteri /./-/_ " -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Il nome utente appena scelto è già utilizzato." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "L'email inserita è già utilizzata." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Il token non corrisponde a nessun invito valido" -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "L'Utente è già registrato." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "L'utente risulta già associato." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Errore nella creazione dell'utente." @@ -586,17 +586,17 @@ msgid "It contain invalid custom fields." msgstr "Contiene campi personalizzati invalidi." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Il nome del progetto è duplicato" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Errore nella creazione del dump di progetto" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Errore nel caricamento del dump di progetto" @@ -900,7 +900,7 @@ msgid "Authentication required" msgstr "E' richiesta l'autenticazione" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -919,8 +919,8 @@ msgid "web" msgstr "web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -935,8 +935,8 @@ msgstr "Url successivo" msgid "secret key for ciphering the application tokens" msgstr "chiave segreta per cifrare i token dell'applicazione" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "utente" @@ -958,11 +958,12 @@ msgstr "Commento" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "data creata" @@ -1045,26 +1046,26 @@ msgstr "Il progetto non esiste" msgid "Bad signature" msgstr "Firma non valida" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "L'elemento di riferimento non esiste" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Lo stato non esiste" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Lo stato è stato modificato a seguito di un commit di BitBucket" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Informazione sul problema non valida" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1084,17 +1085,17 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "Problema creato da BItBucket" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Commento sul problema non valido" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1114,7 +1115,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1396,19 +1397,19 @@ msgstr "Valori dell'amministratore del progetto" msgid "Admin roles" msgstr "Ruoli dell'amministratore" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Il nome del template non è valido" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "La descrizione del template non è valida" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Almeno uno degli utenti deve essere attivo come amministratore" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Non hai il permesso di vedere questo elemento." @@ -1423,7 +1424,7 @@ msgstr "L'ID di progetto non corrisponde tra oggetto e progetto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "proprietario" @@ -1437,8 +1438,8 @@ msgstr "proprietario" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "progetto" @@ -1455,7 +1456,7 @@ msgstr "ID dell'oggetto" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "data modificata" @@ -1477,7 +1478,7 @@ msgstr "non approvato" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "ordine" @@ -1497,14 +1498,18 @@ msgstr "Personalizzato" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Testo" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Testo multi-linea" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1650,17 +1655,17 @@ msgstr "Da:" msgid "To:" msgstr "A:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "contenuto" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "nota bloccata" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "sprint" @@ -1726,10 +1731,23 @@ msgstr "assegnato a" msgid "external reference" msgstr "referenza esterna" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "conta" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "lumaca" @@ -1977,12 +1995,12 @@ msgstr "notifica utenti" msgid "Watched" msgstr "Osservato" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "La notifica esiste per l'utente e il progetto specificati" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "Valore non valido per il livello di notifica" @@ -2867,47 +2885,47 @@ msgstr "versione" msgid "You can't leave the project if there are no more owners" msgstr "Non puoi abbandonare il progetto se non ci sono altri proprietari" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "L'indirizzo email è già usato" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Ruolo di progetto non valido" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Opzioni predefinite" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Stati della storia utente" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Punti" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Stati del compito" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Stati del problema" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Tipologie del problema" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Priorità" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Criticità" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Ruoli" @@ -3421,10 +3439,6 @@ msgstr "Non c'è nessuno stato della storia utente con questo ID" msgid "There's no task status with that id" msgstr "Non c'è nessuno stato del compito con questo ID" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "conta" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3442,11 +3456,11 @@ msgstr "il parametro 'contenuto' è obbligatorio" msgid "'project_id' parameter is mandatory" msgstr "Il parametro 'ID progetto' è obbligatorio" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "ultima modificatore" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3466,58 +3480,58 @@ msgstr "Permessi" msgid "Important dates" msgstr "Date importanti" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Username o e-mail non validi" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Mail inviata con successo!" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Token non valido" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "E' necessario il parametro della password corrente" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "E' necessario il parametro della nuovo password" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Lunghezza della password non valida, sono necessari almeno 6 caratteri" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Password corrente non valida" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Argomento non valido" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Formato dell'immagine non valido" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "E-mail duplicata" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "E-mail non valida" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Username o e-mail non validi" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Mail inviata con successo!" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Token non valido" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "E' necessario il parametro della password corrente" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "E' necessario il parametro della nuovo password" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Lunghezza della password non valida, sono necessari almeno 6 caratteri" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Password corrente non valida" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Argomento non valido" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Formato dell'immagine non valido" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Non valido. Sei sicuro che il token sia corretto e che tu non l'abbia già " "usato in precedenza?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Non valido. Sicuro che il token sia corretto?" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 8c7e29a7..a5236317 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,9 +9,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" "MIME-Version: 1.0\n" @@ -41,27 +41,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Verplicht. 255 tekens of minder. Letters, nummers en /./-/_ tekens'" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Gebruikersnaame is al in gebruik." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "E-mail adres is al in gebruik." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Token stemt niet overeen met een geldige uitnodiging." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Gebruiker is al geregistreerd." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Lidmaatschap met gebruiker bestaat al." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Fout bij het aanmaken van een nieuwe gebruiker." @@ -575,17 +575,17 @@ msgid "It contain invalid custom fields." msgstr "Het bevat ongeldige eigen velden:" #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Naam gedupliceerd voor het project" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Fout bij genereren project dump" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Fout bij laden project dump" @@ -764,7 +764,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -783,8 +783,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -799,8 +799,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -822,11 +822,12 @@ msgstr "commentaar" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "aanmaakdatum" @@ -904,26 +905,26 @@ msgstr "Het project bestaat niet" msgid "Bad signature" msgstr "Slechte signature" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Het element waarnaar verwezen wordt bestaat niet" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "De status bestaat niet" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Status veranderd door Bitbucket commit" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Ongeldige issue informatie" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -934,17 +935,17 @@ msgid "" "{description}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Ongeldige issue commentaar informatie" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -955,7 +956,7 @@ msgid "" "{message}" msgstr "" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1197,19 +1198,19 @@ msgstr "Admin project waarden" msgid "Admin roles" msgstr "Admin rollen" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Ongeldige template naam" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Ongeldige template omschrijving" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Minstens één van de gebruikers moet een active admin zijn" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Je hebt geen toestamming om dat te bekijken." @@ -1224,7 +1225,7 @@ msgstr "Project ID van object is niet gelijk aan die van het project" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "eigenaar" @@ -1238,8 +1239,8 @@ msgstr "eigenaar" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "project" @@ -1256,7 +1257,7 @@ msgstr "object id" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "gemodifieerde datum" @@ -1278,7 +1279,7 @@ msgstr "is verouderd" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "volgorde" @@ -1298,14 +1299,18 @@ msgstr "" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1451,17 +1456,17 @@ msgstr "Van:" msgid "To:" msgstr "Naar:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "inhoud" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "geblokkeerde notitie" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "" @@ -1529,10 +1534,23 @@ msgstr "toegewezen aan" msgid "external reference" msgstr "externe referentie" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1779,12 +1797,12 @@ msgstr "verwittig gebruikers" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "Verwittiging bestaat voor gespecifieerde gebruiker en project" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2313,47 +2331,47 @@ msgstr "versie" msgid "You can't leave the project if there are no more owners" msgstr "Je kan het project niet verlaten als er geen andere eigenaars zijn" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "E-mail adres is al in gebruik" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Ongeldige rol voor project" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Standaard opties" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Status van User story" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Punten" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Statussen van taken" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Statussen van Issues" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Types van issue" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Prioriteiten" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Ernstniveaus" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Rollen" @@ -2819,10 +2837,6 @@ msgstr "Er is geen user story status met dat id" msgid "There's no task status with that id" msgstr "Er is geen taak status met dat id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -2840,11 +2854,11 @@ msgstr "'inhoud' parameter is verplicht" msgid "'project_id' parameter is mandatory" msgstr "'project_id' parameter is verplicht" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "gebruiker met laatste wijziging" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -2864,56 +2878,56 @@ msgstr "Toestemmingen" msgid "Important dates" msgstr "Belangrijke data" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Ongeldige gebruikersnaam of e-mail" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Mail met succes verzonden!" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Token is ongeldig" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Huidig wachtwoord parameter vereist" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Nieuw wachtwoord parameter vereist" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Ongeldige lengte van wachtwoord, minstens 6 tekens vereist" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Ongeldig huidig wachtwoord" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Onvolledige argumenten" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Ongeldig afbeelding formaat" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Gedupliceerde e-mail" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Ongeldige e-mail" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Ongeldige gebruikersnaam of e-mail" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Mail met succes verzonden!" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Token is ongeldig" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Huidig wachtwoord parameter vereist" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Nieuw wachtwoord parameter vereist" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Ongeldige lengte van wachtwoord, minstens 6 tekens vereist" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Ongeldig huidig wachtwoord" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Onvolledige argumenten" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Ongeldig afbeelding formaat" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "Ongeldig, weet je zeker dat het token correct en ongebruikt is?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Ongeldig, weet je zeker dat het token correct is?" diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index bbc893c3..ec0ecbfb 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -10,9 +10,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-10-12 08:24+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" "MIME-Version: 1.0\n" @@ -43,27 +43,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Wymagane. Maksymalnie 255 znaków. Litery, cyfry oraz /./-/_ " -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Nazwa użytkownika jest już używana." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Ten adres email jest już w użyciu." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Token nie zgadza się z żadnym zaproszeniem" -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Użytkownik już zarejestrowany" -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Członkowstwo z użytkownikiem już istnieje." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Błąd przy tworzeniu użytkownika." @@ -576,17 +576,17 @@ msgid "It contain invalid custom fields." msgstr "Zawiera niewłaściwe pola niestandardowe." #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nazwa projektu zduplikowana" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Błąd w trakcie generowania zrzutu projektu" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Błąd w trakcie wczytywania zrzutu projektu" @@ -832,7 +832,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -851,8 +851,8 @@ msgid "web" msgstr "web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -867,8 +867,8 @@ msgstr "Następny url" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "użytkownik" @@ -890,11 +890,12 @@ msgstr "komentarz" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "data utworzenia" @@ -972,26 +973,26 @@ msgstr "Projekt nie istnieje" msgid "Bad signature" msgstr "Błędna sygnatura" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Element referencyjny nie istnieje" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Status nie istnieje" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Status zmieniony przez commit z BitBucket" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Nieprawidłowa informacja o zgłoszeniu" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1008,17 +1009,17 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "Zgłoszenie utworzone przez BitBucket." -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Nieprawidłowa informacja o komentarzu do zgłoszenia" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1035,7 +1036,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1305,19 +1306,19 @@ msgstr "Administruj wartościami projektu" msgid "Admin roles" msgstr "Administruj rolami" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Nieprawidłowa nazwa szablonu" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Nieprawidłowy opis szablonu" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Przynajmniej jeden użytkownik musi być aktywnym Administratorem" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Nie masz uprawnień by to zobaczyć." @@ -1332,7 +1333,7 @@ msgstr "ID nie pasuje pomiędzy obiektem a projektem" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "właściciel" @@ -1346,8 +1347,8 @@ msgstr "właściciel" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "projekt" @@ -1364,7 +1365,7 @@ msgstr "id obiektu" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "data modyfikacji" @@ -1386,7 +1387,7 @@ msgstr "jest przestarzałe" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "kolejność" @@ -1406,14 +1407,18 @@ msgstr "Niestandardowy" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Tekst" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Teks wielowierszowy" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1559,17 +1564,17 @@ msgstr "Od:" msgid "To:" msgstr "Do:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "zawartość" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "zaglokowana notatka" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "sprint" @@ -1635,10 +1640,23 @@ msgstr "przypisane do" msgid "external reference" msgstr "źródło zgłoszenia" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "ilość" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1885,12 +1903,12 @@ msgstr "powiadom użytkowników" msgid "Watched" msgstr "Obserwowane" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "Powiadomienie istnieje dla określonego użytkownika i projektu" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "Nieprawidłowa wartość dla poziomu notyfikacji" @@ -2650,47 +2668,47 @@ msgstr "wersja" msgid "You can't leave the project if there are no more owners" msgstr "Nie możesz opuścić projektu, jeśli jesteś jego jedynym właścicielem" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Tena adres e-mail jest już w użyciu" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Nieprawidłowa rola w projekcie" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Domyślne opcje" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Statusy historyjek użytkownika" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Punkty" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Statusy zadań" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Statusy zgłoszeń" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Typu zgłoszeń" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Priorytety" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Ważność" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Role" @@ -3186,10 +3204,6 @@ msgstr "Nie ma statusu historyjki użytkownika z takim ID" msgid "There's no task status with that id" msgstr "Nie ma statusu zadania z takim ID" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "ilość" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3207,11 +3221,11 @@ msgstr "Parametr 'zawartość' jest wymagany" msgid "'project_id' parameter is mandatory" msgstr "Parametr 'id_projektu' jest wymagany" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "ostatnio zmodyfikowane przez" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3231,60 +3245,60 @@ msgstr "Uprawnienia" msgid "Important dates" msgstr "Ważne daty" -#: taiga/users/api.py:150 taiga/users/api.py:157 +#: taiga/users/api.py:111 +msgid "Duplicated email" +msgstr "Zduplikowany adres e-mail" + +#: taiga/users/api.py:113 +msgid "Not valid email" +msgstr "Niepoprawny adres e-mail" + +#: taiga/users/api.py:146 taiga/users/api.py:153 msgid "Invalid username or email" msgstr "Nieprawidłowa nazwa użytkownika lub adrs e-mail" -#: taiga/users/api.py:166 +#: taiga/users/api.py:161 msgid "Mail sended successful!" msgstr "E-mail wysłany poprawnie!" -#: taiga/users/api.py:178 taiga/users/api.py:183 +#: taiga/users/api.py:173 taiga/users/api.py:178 msgid "Token is invalid" msgstr "Nieprawidłowy token." -#: taiga/users/api.py:204 +#: taiga/users/api.py:199 msgid "Current password parameter needed" msgstr "Należy podać bieżące hasło" -#: taiga/users/api.py:207 +#: taiga/users/api.py:202 msgid "New password parameter needed" msgstr "Należy podać nowe hasło" -#: taiga/users/api.py:210 +#: taiga/users/api.py:205 msgid "Invalid password length at least 6 charaters needed" msgstr "" "Nieprawidłowa długość hasła - wymagane jest co najmniej 6 znaków" -#: taiga/users/api.py:213 +#: taiga/users/api.py:208 msgid "Invalid current password" msgstr "Podałeś nieprawidłowe bieżące hasło" -#: taiga/users/api.py:229 +#: taiga/users/api.py:224 msgid "Incomplete arguments" msgstr "Pola niekompletne" -#: taiga/users/api.py:234 +#: taiga/users/api.py:229 msgid "Invalid image format" msgstr "Niepoprawny format obrazka" -#: taiga/users/api.py:279 -msgid "Duplicated email" -msgstr "Zduplikowany adres e-mail" - -#: taiga/users/api.py:281 -msgid "Not valid email" -msgstr "Niepoprawny adres e-mail" - -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Niepoprawne, jesteś pewien, że token jest poprawny i nie używałeś go " "wcześniej? " -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Niepoprawne, jesteś pewien, że token jest poprawny?" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 6b644639..6dea9fca 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -18,9 +18,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-10-11 01:08+0000\n" -"Last-Translator: Walker de Alencar \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" "MIME-Version: 1.0\n" @@ -50,27 +50,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "Obrigatório. No máximo 255 caracteres. Letras, números e /./-/_ ." -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "Nome de usuário já está em uso." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Este e-mail já está em uso." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "Esse token não bate com nenhum convite." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Este usuário já está registrado." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Esse usuário já é membro." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Erro ao criar um novo usuário." @@ -583,17 +583,17 @@ msgid "It contain invalid custom fields." msgstr "Contém campos personalizados inválidos" #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Nome duplicado para o projeto" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Erro gerando arquivo de restauração do projeto" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Erro carregando arquivo de restauração do projeto" @@ -838,7 +838,7 @@ msgid "Authentication required" msgstr "Autenticação necessária" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -857,8 +857,8 @@ msgid "web" msgstr "web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -873,8 +873,8 @@ msgstr "Próxima url" msgid "secret key for ciphering the application tokens" msgstr "chave secreta para cifrar os tokens da aplicação" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "usuário" @@ -896,11 +896,12 @@ msgstr "comentário" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "data de criação" @@ -978,26 +979,26 @@ msgstr "O projeto não existe" msgid "Bad signature" msgstr "Assinatura Ruim" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "O elemento referenciado não existe" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "O estatus não existe" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Status alterado em Bitbucket commit" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "Informação de caso inválida" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1014,17 +1015,17 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "Caso criado pelo Bitbucket." -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "Informação de comentário de caso inválido" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1041,7 +1042,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1310,19 +1311,19 @@ msgstr "Valores projeto admin" msgid "Admin roles" msgstr "Funções Admin" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Nome de template inválido" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Descrição de template inválida" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "Pelo menos one dos usuários deve ser um administrador ativo" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "Você não tem permissão para ver isso" @@ -1337,7 +1338,7 @@ msgstr "ID do projeto não combina entre objeto e projeto" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "dono" @@ -1351,8 +1352,8 @@ msgstr "dono" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "projeto" @@ -1369,7 +1370,7 @@ msgstr "identidade de objeto" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "data modificação" @@ -1391,7 +1392,7 @@ msgstr "está obsoleto" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "ordem" @@ -1411,14 +1412,18 @@ msgstr "Personalizado" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Texto" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Multi-linha" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1564,17 +1569,17 @@ msgstr "De:" msgid "To:" msgstr "Para:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "conteúdo" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "nota bloqueada" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "sprint" @@ -1640,10 +1645,23 @@ msgstr "assinado a" msgid "external reference" msgstr "referência externa" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "contagem" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "slug" @@ -1890,12 +1908,12 @@ msgstr "notificar usuário" msgid "Watched" msgstr "Observado" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "Existe notificação para usuário e projeto especifcado" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "Valor inválido para nível de notificação" @@ -2633,47 +2651,47 @@ msgstr "versão" msgid "You can't leave the project if there are no more owners" msgstr "Você não pode deixar o projeto se não há mais donos" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Endereço de e-mail já utilizado" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Função inválida para projeto" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Opções padrão" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Status de user story" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Pontos" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Status de tarefas" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "Status de casos" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "Tipos de casos" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Prioridades" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Severidades" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Funções" @@ -3166,10 +3184,6 @@ msgstr "Não há status de user story com este id" msgid "There's no task status with that id" msgstr "Não há status de tarega com este id" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "contagem" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3187,11 +3201,11 @@ msgstr "parâmetro 'conteúdo' é mandatório" msgid "'project_id' parameter is mandatory" msgstr "parametro 'project_id' é mandatório" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "último modificador" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3211,58 +3225,58 @@ msgstr "Permissões" msgid "Important dates" msgstr "Datas importantes" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Usuário ou e-mail inválido" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "E-mail enviado com sucesso" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Token é inválido" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Parâmetro de senha atual necessário" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Parâmetro de nova senha necessário" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Senha atual inválida" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Argumentos incompletos" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Formato de imagem inválida" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "E-mail duplicado" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Não é um e-mail válido" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Usuário ou e-mail inválido" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "E-mail enviado com sucesso" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Token é inválido" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Parâmetro de senha atual necessário" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Parâmetro de nova senha necessário" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Comprimento de senha inválido, pelo menos 6 caracteres necessários" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Senha atual inválida" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Argumentos incompletos" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Formato de imagem inválida" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "" "Inválido, você está certo que o token está correto e não foi usado " "anteriormente?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Inválido, tem certeza que o token está correto?" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index d79db4bb..47089590 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-29 13:52+0000\n" -"Last-Translator: Dmitriy Volkov \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" "MIME-Version: 1.0\n" @@ -31,7 +31,7 @@ msgstr "Публичная регистрация отключена." #: taiga/auth/api.py:132 msgid "invalid register type" -msgstr "неправильный тип регистра" +msgstr "неправильный тип регистрации" #: taiga/auth/api.py:145 msgid "invalid login type" @@ -44,36 +44,36 @@ msgstr "неправильное имя пользователя" #: taiga/auth/serializers.py:39 taiga/users/serializers.py:67 msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" -msgstr "Обязательно. 255 символов или меньше. Буквы, числа и символы /./-/_ '" +msgstr "Обязательно. 255 символов или меньше. Буквы, числа и символы /./-/_" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." -msgstr "Это имя пользователя уже используется" +msgstr "Это имя пользователя уже используется." -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "Этот адрес почты уже используется." -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." -msgstr "Идентификатор не подходит ни под одно корректное приглашение." +msgstr "Токен не подходит ни под одно корректное приглашение." -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "Пользователь уже зарегистрирован." -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "Членство с этим пользователем уже существует." -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "Ошибка при создании нового пользователя." #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54 #: taiga/external_apps/services.py:34 msgid "Invalid token" -msgstr "Неверный идентификатор" +msgstr "Неверный токен" #: taiga/base/api/fields.py:268 msgid "This field is required." @@ -550,7 +550,7 @@ msgstr "ошибка импорта вики-ссылок" #: taiga/export_import/dump_service.py:164 msgid "error importing issues" -msgstr "ошибка импорта задач" +msgstr "ошибка импорта запросов" #: taiga/export_import/dump_service.py:169 msgid "error importing user stories" @@ -583,17 +583,17 @@ msgid "It contain invalid custom fields." msgstr "Содержит неверные специальные поля" #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "Уже есть такое имя для проекта" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "Ошибка создания свалочного файла для проекта" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "Ошибка загрузки свалочного файла проекта" @@ -836,7 +836,7 @@ msgid "Authentication required" msgstr "Необходима аутентификация" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -852,11 +852,11 @@ msgstr "url иконки" #: taiga/external_apps/models.py:36 msgid "web" -msgstr "" +msgstr "веб" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -871,8 +871,8 @@ msgstr "Следующий url" msgid "secret key for ciphering the application tokens" msgstr "секретный ключ для шифрования токенов приложения" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "пользователь" @@ -894,11 +894,12 @@ msgstr "комментарий" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "дата создания" @@ -976,26 +977,26 @@ msgstr "Проект не существует" msgid "Bad signature" msgstr "Плохая подпись" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "Указанный элемент не существует" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "Статус не существует" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "Статус изменён из-за вклада с BitBucket" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" -msgstr "Неверная информация о задаче" +msgstr "Неверная информация о запросе" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1005,24 +1006,24 @@ msgid "" "\n" "{description}" msgstr "" -"Задача создана [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " +"Запрос создан [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " "профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" -"Изначальная задача на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"Изначальный запрос на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " "\"Перейти к 'bb#{number} - {subject}'\"):\n" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." -msgstr "Задача создана из BitBucket." +msgstr "Запрос создан из BitBucket." -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" -msgstr "Неправильная информация в комментарии к задаче" +msgstr "Неправильная информация в комментарии к запросу" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1034,12 +1035,12 @@ msgid "" msgstr "" "Комментарий от [@{bitbucket_user_name}]({bitbucket_user_url} \"Посмотреть " "профиль @{bitbucket_user_name} на BitBucket\") на BitBucket.\n" -"Изначальная задача на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " +"Изначальный запрос на BitBucket: [bb#{number} - {subject}]({bitbucket_url} " "\"Перейти к 'bb#{number} - {subject}'\")\n" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1076,16 +1077,16 @@ msgid "" "\n" "{description}" msgstr "" -"Задача создана [@{github_user_name}]({github_user_url} \"Посмотреть профиль " +"Запрос создана [@{github_user_name}]({github_user_url} \"Посмотреть профиль " "@{github_user_name} на GitHub\") из GitHub.\n" -"Исходная задача на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " +"Исходный запрос на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " "к 'gh#{number} - {subject}'\"):\n" "\n" "{description}" #: taiga/hooks/github/event_hooks.py:168 msgid "Issue created from GitHub." -msgstr "Задача создана из GitHub." +msgstr "Запрос создан из GitHub." #: taiga/hooks/github/event_hooks.py:200 #, python-brace-format @@ -1099,7 +1100,7 @@ msgid "" msgstr "" "Комментарий от [@{github_user_name}]({github_user_url} \"Посмотреть профиль " "@{github_user_name} на GitHub\") из GitHub.\n" -"Исходная задача на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " +"Исходный запрос на GitHub: [gh#{number} - {subject}]({github_url} \"Перейти " "к 'gh#{number} - {subject}'\")\n" "\n" "{message}" @@ -1135,7 +1136,7 @@ msgid "" msgstr "" "Комментарий от [@{gitlab_user_name}]({gitlab_user_url} \"Посмотреть профиль " "@{gitlab_user_name} на GitLab\") из GitLab.\n" -"Исходная задача на GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go to " +"Исходный запрос на GitLab: [gl#{number} - {subject}]({gitlab_url} \"Go to " "'gl#{number} - {subject}'\")\n" "\n" "{message}" @@ -1173,7 +1174,7 @@ msgstr "Просмотреть задачи" #: taiga/permissions/permissions.py:25 taiga/permissions/permissions.py:34 #: taiga/permissions/permissions.py:68 msgid "View issues" -msgstr "Посмотреть задачи" +msgstr "Посмотреть запросы" #: taiga/permissions/permissions.py:26 taiga/permissions/permissions.py:36 #: taiga/permissions/permissions.py:73 @@ -1203,11 +1204,11 @@ msgstr "Добавить комментарии к задачам" #: taiga/permissions/permissions.py:42 msgid "Add issues" -msgstr "Добавить задачи" +msgstr "Добавить запросы" #: taiga/permissions/permissions.py:43 msgid "Add comments to issues" -msgstr "Добавить комментарии к задачам" +msgstr "Добавить комментарии к запросам" #: taiga/permissions/permissions.py:44 taiga/permissions/permissions.py:74 msgid "Add wiki page" @@ -1267,15 +1268,15 @@ msgstr "Удалить задачу" #: taiga/permissions/permissions.py:69 msgid "Add issue" -msgstr "Добавить задачу" +msgstr "Добавить запрос" #: taiga/permissions/permissions.py:70 msgid "Modify issue" -msgstr "Изменить задачу" +msgstr "Изменить запрос" #: taiga/permissions/permissions.py:71 msgid "Delete issue" -msgstr "Удалить задачу" +msgstr "Удалить запрос" #: taiga/permissions/permissions.py:76 msgid "Delete wiki page" @@ -1309,20 +1310,20 @@ msgstr "Управлять значениями проекта" msgid "Admin roles" msgstr "Управлять ролями" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "Неверное название шаблона" -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "Неверное описание шаблона" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "" "По крайней мере один пользователь должен быть активным администратором." -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "У вас нет разрешения на просмотр." @@ -1337,7 +1338,7 @@ msgstr "Идентификатор проекта не подходит к эт #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "владелец" @@ -1351,8 +1352,8 @@ msgstr "владелец" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "проект" @@ -1369,7 +1370,7 @@ msgstr "идентификатор объекта" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "изменённая дата" @@ -1391,7 +1392,7 @@ msgstr "устаревшее" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "порядок" @@ -1411,14 +1412,18 @@ msgstr "Специальный" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "Текст" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "Многострочный текст" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1439,7 +1444,7 @@ msgstr "задача" #: taiga/projects/custom_attributes/models.py:127 msgid "issue" -msgstr "задача" +msgstr "запрос" #: taiga/projects/custom_attributes/serializers.py:57 msgid "Already exists one with the same name." @@ -1564,41 +1569,43 @@ msgstr "От:" msgid "To:" msgstr "Кому:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "содержимое" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "Заметка о блокировке" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "спринт" #: taiga/projects/issues/api.py:160 msgid "You don't have permissions to set this sprint to this issue." -msgstr "У вас нет прав для того чтобы установить такой спринт для этой задачи" +msgstr "" +"У вас нет прав для того чтобы установить такой спринт для этого запроса" #: taiga/projects/issues/api.py:164 msgid "You don't have permissions to set this status to this issue." -msgstr "У вас нет прав для того чтобы установить такой статус для этой задачи" +msgstr "" +"У вас нет прав для того чтобы установить такой статус для этого запроса" #: taiga/projects/issues/api.py:168 msgid "You don't have permissions to set this severity to this issue." msgstr "" -"У вас нет прав для того чтобы установить такую важность для этой задачи" +"У вас нет прав для того чтобы установить такую важность для этого запроса" #: taiga/projects/issues/api.py:172 msgid "You don't have permissions to set this priority to this issue." msgstr "" -"У вас нет прав для того чтобы установить такой приоритет для этой задачи" +"У вас нет прав для того чтобы установить такой приоритет для этого запроса" #: taiga/projects/issues/api.py:176 msgid "You don't have permissions to set this type to this issue." -msgstr "У вас нет прав для того чтобы установить такой тип для этой задачи" +msgstr "У вас нет прав для того чтобы установить такой тип для этого запроса" #: taiga/projects/issues/models.py:36 taiga/projects/tasks/models.py:35 #: taiga/projects/userstories/models.py:57 @@ -1642,10 +1649,23 @@ msgstr "назначено" msgid "external reference" msgstr "внешняя ссылка" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "количество" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "ссылочное имя" @@ -1735,11 +1755,11 @@ msgstr "важность по умолчанию" #: taiga/projects/models.py:122 msgid "default issue status" -msgstr "статус задачи по умолчанию" +msgstr "статус запроса по умолчанию" #: taiga/projects/models.py:126 msgid "default issue type" -msgstr "тип задачи по умолчанию" +msgstr "тип запроса по умолчанию" #: taiga/projects/models.py:147 msgid "members" @@ -1767,7 +1787,7 @@ msgstr "активная wiki-панель" #: taiga/projects/models.py:160 taiga/projects/models.py:620 msgid "active issues panel" -msgstr "активная панель задач" +msgstr "панель активных запросов" #: taiga/projects/models.py:163 taiga/projects/models.py:623 msgid "videoconference system" @@ -1843,11 +1863,11 @@ msgstr "статусы задач" #: taiga/projects/models.py:631 msgid "issue statuses" -msgstr "статусы задач" +msgstr "статусы запросов" #: taiga/projects/models.py:632 msgid "issue types" -msgstr "типы задач" +msgstr "типы запросов" #: taiga/projects/models.py:633 msgid "priorities" @@ -1894,12 +1914,12 @@ msgstr "уведомить пользователей" msgid "Watched" msgstr "Просмотренные" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "Уведомление существует для данных пользователя и проекта" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "Неверное значение для уровня уведомлений" @@ -1916,12 +1936,12 @@ msgid "" " " msgstr "" "\n" -"

Задача обновлена

\n" -"

Привет %(user)s,
%(changer)s обновил(а) задачу в %(project)sЗапрос обновлён\n" +"

Привет %(user)s,
%(changer)s обновил(а) запрос в %(project)s\n" -"

Задача #%(ref)s %(subject)s

\n" -" Просмотреть задачу\n" +"

Запрос #%(ref)s %(subject)s

\n" +" Просмотреть запрос\n" " " #: taiga/projects/notifications/templates/emails/issues/issue-change-body-text.jinja:3 @@ -1933,9 +1953,9 @@ msgid "" "See issue #%(ref)s %(subject)s at %(url)s\n" msgstr "" "\n" -"Задача обновлена\n" -"Привет %(user)s, %(changer)s обновил(а) задачу %(project)s\n" -"Просмотреть задачу #%(ref)s %(subject)s можно по ссылке %(url)s\n" +"Запрос обновлён\n" +"Привет %(user)s, %(changer)s обновил(а) запрос %(project)s\n" +"Просмотреть запрос #%(ref)s %(subject)s можно по ссылке %(url)s\n" #: taiga/projects/notifications/templates/emails/issues/issue-change-subject.jinja:1 #, python-format @@ -1944,7 +1964,7 @@ msgid "" "[%(project)s] Updated the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Обновлена задача #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Обновлён запрос #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-create-body-html.jinja:4 #, python-format @@ -1960,12 +1980,12 @@ msgid "" " " msgstr "" "\n" -"

Добавлена новая задача

\n" -"

Привет %(user)s,
%(changer)s добавил(а) новую задачу в " +"

Добавлен новый запрос

\n" +"

Привет %(user)s,
%(changer)s добавил(а) новый запрос в " "%(project)s

\n" -"

Задача #%(ref)s %(subject)s

\n" -" Просмотреть задачу\n" +"

Запрос #%(ref)s %(subject)s

\n" +" Просмотреть запрос\n" "

Команда Тайги

\n" " " @@ -1981,9 +2001,9 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Добавлена новая задача\n" -"Привет %(user)s, %(changer)s добавил(а) новую задачу в %(project)s\n" -"Просмотреть задачу #%(ref)s %(subject)s можно по ссылке %(url)s\n" +"Добавлен новый запрос\n" +"Привет %(user)s, %(changer)s добавил(а) новый запрос в %(project)s\n" +"Просмотреть запрос #%(ref)s %(subject)s можно по ссылке %(url)s\n" "\n" "---\n" "Команда Тайги\n" @@ -1995,7 +2015,7 @@ msgid "" "[%(project)s] Created the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Добавлена задача #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Добавлен запрос #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/issues/issue-delete-body-html.jinja:4 #, python-format @@ -2009,10 +2029,10 @@ msgid "" " " msgstr "" "\n" -"

Задача удалена

\n" -"

Привет %(user)s,
%(changer)s удалил(а) задачу из %(project)sЗапрос удалён\n" +"

Привет %(user)s,
%(changer)s удалил(а) запрос из %(project)s\n" -"

Задача #%(ref)s %(subject)s

\n" +"

Запрос #%(ref)s %(subject)s

\n" "

Команда Тайги

\n" " " @@ -2028,9 +2048,9 @@ msgid "" "The Taiga Team\n" msgstr "" "\n" -"Задача удалена\n" -"Привет %(user)s, %(changer)s удалил(а) задачу из %(project)s\n" -"Задача #%(ref)s %(subject)s\n" +"Запрос удалён\n" +"Привет %(user)s, %(changer)s удалил(а) запрос из %(project)s\n" +"Запрос #%(ref)s %(subject)s\n" "\n" "---\n" "Команда Тайги\n" @@ -2042,7 +2062,7 @@ msgid "" "[%(project)s] Deleted the issue #%(ref)s \"%(subject)s\"\n" msgstr "" "\n" -"[%(project)s] Удалена задача #%(ref)s \"%(subject)s\"\n" +"[%(project)s] Удалён запрос #%(ref)s \"%(subject)s\"\n" #: taiga/projects/notifications/templates/emails/milestones/milestone-change-body-html.jinja:4 #, python-format @@ -2645,47 +2665,47 @@ msgstr "версия" msgid "You can't leave the project if there are no more owners" msgstr "Вы не можете покинуть проект если в нём нет других владельцев" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "Этот почтовый адрес уже используется" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "Неверная роль для этого проекта" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "Параметры по умолчанию" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "Статусу пользовательских историй" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "Очки" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "Статусы задачи" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" -msgstr "Статусы задачи" +msgstr "Статусы запроса" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" -msgstr "Типы задачи" +msgstr "Типы запроса" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "Приоритеты" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "Степени важности" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "Роли" @@ -3166,7 +3186,7 @@ msgstr "является требованием команды" #: taiga/projects/userstories/models.py:102 msgid "generated from issue" -msgstr "создано из задачи" +msgstr "создано из запроса" #: taiga/projects/userstories/validators.py:28 msgid "There's no user story with that id" @@ -3184,10 +3204,6 @@ msgstr "Не существует статуса пользовательско msgid "There's no task status with that id" msgstr "Не существует статуса задачи с таким идентификатором" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "количество" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3205,11 +3221,11 @@ msgstr "параметр 'content' является обязательным" msgid "'project_id' parameter is mandatory" msgstr "параметр 'project_id' является обязательным" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "последний отредактировавший" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3229,56 +3245,56 @@ msgstr "Права доступа" msgid "Important dates" msgstr "Важные даты" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "Неверное имя пользователя или e-mail" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "Письмо успешно отправлено!" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "Неверный токен" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "Поле \"текущий пароль\" является обязательным" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "Поле \"новый пароль\" является обязательным" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "Неверная длина пароля, требуется как минимум 6 символов" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "Неверно указан текущий пароль" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "Список аргументов неполон" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "Неправильный формат изображения" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "Этот email уже используется" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "Невалидный email" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "Неверное имя пользователя или e-mail" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "Письмо успешно отправлено!" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "Неверный токен" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "Поле \"текущий пароль\" является обязательным" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "Поле \"новый пароль\" является обязательным" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "Неверная длина пароля, требуется как минимум 6 символов" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "Неверно указан текущий пароль" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "Список аргументов неполон" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "Неправильный формат изображения" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "Неверно, вы уверены что токен правильный и не использовался ранее?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "Неверно, вы уверены что токен правильный?" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 1bf09ea4..3ad1b75d 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,9 +11,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-09-26 19:35+0200\n" -"PO-Revision-Date: 2015-09-26 17:36+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" "MIME-Version: 1.0\n" @@ -43,27 +43,27 @@ msgid "" "Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'" msgstr "必填。最多255字元(可為數字,字母,符號....)" -#: taiga/auth/services.py:75 +#: taiga/auth/services.py:73 msgid "Username is already in use." msgstr "本用戶名稱已被註冊" -#: taiga/auth/services.py:78 +#: taiga/auth/services.py:76 msgid "Email is already in use." msgstr "本電子郵件已使用" -#: taiga/auth/services.py:94 +#: taiga/auth/services.py:92 msgid "Token not matches any valid invitation." msgstr "代碼與任何有效的邀請不相符" -#: taiga/auth/services.py:122 +#: taiga/auth/services.py:120 msgid "User is already registered." msgstr "使用者已被註冊。" -#: taiga/auth/services.py:146 +#: taiga/auth/services.py:144 msgid "Membership with user is already exists." msgstr "使用者的成員資格已存在。" -#: taiga/auth/services.py:172 +#: taiga/auth/services.py:170 msgid "Error on creating new user." msgstr "無法創建新使用者" @@ -567,17 +567,17 @@ msgid "It contain invalid custom fields." msgstr "包括無效慣例欄位" #: taiga/export_import/serializers.py:513 -#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:72 -#: taiga/projects/serializers.py:98 taiga/projects/serializers.py:129 -#: taiga/projects/serializers.py:172 +#: taiga/projects/milestones/serializers.py:64 taiga/projects/serializers.py:70 +#: taiga/projects/serializers.py:96 taiga/projects/serializers.py:127 +#: taiga/projects/serializers.py:170 msgid "Name duplicated for the project" msgstr "專案的名稱被複製了" -#: taiga/export_import/tasks.py:55 taiga/export_import/tasks.py:56 +#: taiga/export_import/tasks.py:53 taiga/export_import/tasks.py:54 msgid "Error generating project dump" msgstr "產生專案傾倒時出錯" -#: taiga/export_import/tasks.py:89 taiga/export_import/tasks.py:90 +#: taiga/export_import/tasks.py:85 taiga/export_import/tasks.py:86 msgid "Error loading project dump" msgstr "載入專案傾倒時出錯" @@ -819,7 +819,7 @@ msgid "Authentication required" msgstr "" #: taiga/external_apps/models.py:33 -#: taiga/projects/custom_attributes/models.py:36 +#: taiga/projects/custom_attributes/models.py:34 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:134 #: taiga/projects/models.py:394 taiga/projects/models.py:433 #: taiga/projects/models.py:458 taiga/projects/models.py:495 @@ -838,8 +838,8 @@ msgid "web" msgstr "" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 -#: taiga/projects/custom_attributes/models.py:37 -#: taiga/projects/history/templatetags/functions.py:25 +#: taiga/projects/custom_attributes/models.py:35 +#: taiga/projects/history/templatetags/functions.py:23 #: taiga/projects/issues/models.py:61 taiga/projects/models.py:138 #: taiga/projects/models.py:603 taiga/projects/tasks/models.py:60 #: taiga/projects/userstories/models.py:90 @@ -854,8 +854,8 @@ msgstr "" msgid "secret key for ciphering the application tokens" msgstr "" -#: taiga/external_apps/models.py:55 taiga/projects/notifications/models.py:84 -#: taiga/projects/votes/models.py:50 +#: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 +#: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" msgstr "" @@ -877,11 +877,12 @@ msgstr "評論" #: taiga/feedback/models.py:29 taiga/projects/attachments/models.py:61 #: taiga/projects/custom_attributes/models.py:44 -#: taiga/projects/issues/models.py:53 taiga/projects/milestones/models.py:45 -#: taiga/projects/models.py:140 taiga/projects/models.py:605 -#: taiga/projects/notifications/models.py:86 taiga/projects/tasks/models.py:46 -#: taiga/projects/userstories/models.py:82 taiga/projects/votes/models.py:52 -#: taiga/projects/wiki/models.py:38 taiga/userstorage/models.py:27 +#: taiga/projects/issues/models.py:53 taiga/projects/likes/models.py:52 +#: taiga/projects/milestones/models.py:45 taiga/projects/models.py:140 +#: taiga/projects/models.py:605 taiga/projects/notifications/models.py:86 +#: taiga/projects/tasks/models.py:46 taiga/projects/userstories/models.py:82 +#: taiga/projects/votes/models.py:52 taiga/projects/wiki/models.py:39 +#: taiga/userstorage/models.py:27 msgid "created date" msgstr "創建日期" @@ -956,26 +957,26 @@ msgstr "專案不存在" msgid "Bad signature" msgstr "錯誤簽名" -#: taiga/hooks/bitbucket/event_hooks.py:73 taiga/hooks/github/event_hooks.py:75 +#: taiga/hooks/bitbucket/event_hooks.py:84 taiga/hooks/github/event_hooks.py:75 #: taiga/hooks/gitlab/event_hooks.py:73 msgid "The referenced element doesn't exist" msgstr "參考元素不存在" -#: taiga/hooks/bitbucket/event_hooks.py:80 taiga/hooks/github/event_hooks.py:82 +#: taiga/hooks/bitbucket/event_hooks.py:91 taiga/hooks/github/event_hooks.py:82 #: taiga/hooks/gitlab/event_hooks.py:80 msgid "The status doesn't exist" msgstr "狀態不存在" -#: taiga/hooks/bitbucket/event_hooks.py:86 +#: taiga/hooks/bitbucket/event_hooks.py:97 msgid "Status changed from BitBucket commit" msgstr "來自BitBucket 投入的狀態更新" -#: taiga/hooks/bitbucket/event_hooks.py:115 +#: taiga/hooks/bitbucket/event_hooks.py:126 #: taiga/hooks/github/event_hooks.py:141 taiga/hooks/gitlab/event_hooks.py:113 msgid "Invalid issue information" msgstr "無效的問題資訊" -#: taiga/hooks/bitbucket/event_hooks.py:131 +#: taiga/hooks/bitbucket/event_hooks.py:142 #, python-brace-format msgid "" "Issue created by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -992,17 +993,17 @@ msgstr "" "\n" "{description}" -#: taiga/hooks/bitbucket/event_hooks.py:142 +#: taiga/hooks/bitbucket/event_hooks.py:153 msgid "Issue created from BitBucket." msgstr "來自BitBucket的問題:" -#: taiga/hooks/bitbucket/event_hooks.py:166 +#: taiga/hooks/bitbucket/event_hooks.py:177 #: taiga/hooks/github/event_hooks.py:177 taiga/hooks/github/event_hooks.py:192 #: taiga/hooks/gitlab/event_hooks.py:152 msgid "Invalid issue comment information" msgstr "無效的議題評論資訊" -#: taiga/hooks/bitbucket/event_hooks.py:174 +#: taiga/hooks/bitbucket/event_hooks.py:185 #, python-brace-format msgid "" "Comment by [@{bitbucket_user_name}]({bitbucket_user_url} \"See " @@ -1019,7 +1020,7 @@ msgstr "" "\n" "{message}" -#: taiga/hooks/bitbucket/event_hooks.py:185 +#: taiga/hooks/bitbucket/event_hooks.py:196 #, python-brace-format msgid "" "Comment From BitBucket:\n" @@ -1285,19 +1286,19 @@ msgstr "管理員專案數值" msgid "Admin roles" msgstr "管理員角色" -#: taiga/projects/api.py:203 +#: taiga/projects/api.py:202 msgid "Not valid template name" msgstr "非有效樣板名稱 " -#: taiga/projects/api.py:206 +#: taiga/projects/api.py:205 msgid "Not valid template description" msgstr "無效樣板描述" -#: taiga/projects/api.py:482 taiga/projects/serializers.py:266 +#: taiga/projects/api.py:481 taiga/projects/serializers.py:264 msgid "At least one of the user must be an active admin" msgstr "至少需有一位使用者擔任管理員" -#: taiga/projects/api.py:512 +#: taiga/projects/api.py:511 msgid "You don't have permisions to see that." msgstr "您無觀看權限" @@ -1312,7 +1313,7 @@ msgstr "專案ID不符合物件與專案" #: taiga/projects/attachments/models.py:52 taiga/projects/issues/models.py:38 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:145 #: taiga/projects/notifications/models.py:59 taiga/projects/tasks/models.py:37 -#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34 +#: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:35 #: taiga/userstorage/models.py:25 msgid "owner" msgstr "所有者" @@ -1326,8 +1327,8 @@ msgstr "所有者" #: taiga/projects/models.py:551 taiga/projects/models.py:582 #: taiga/projects/notifications/models.py:71 #: taiga/projects/notifications/models.py:88 taiga/projects/tasks/models.py:41 -#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28 -#: taiga/projects/wiki/models.py:66 taiga/users/models.py:211 +#: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:29 +#: taiga/projects/wiki/models.py:67 taiga/users/models.py:211 msgid "project" msgstr "專案" @@ -1344,7 +1345,7 @@ msgstr "物件ID" #: taiga/projects/issues/models.py:56 taiga/projects/milestones/models.py:48 #: taiga/projects/models.py:143 taiga/projects/models.py:608 #: taiga/projects/tasks/models.py:49 taiga/projects/userstories/models.py:85 -#: taiga/projects/wiki/models.py:41 taiga/userstorage/models.py:29 +#: taiga/projects/wiki/models.py:42 taiga/userstorage/models.py:29 msgid "modified date" msgstr "修改日期" @@ -1366,7 +1367,7 @@ msgstr "棄用" #: taiga/projects/models.py:435 taiga/projects/models.py:462 #: taiga/projects/models.py:497 taiga/projects/models.py:520 #: taiga/projects/models.py:545 taiga/projects/models.py:578 -#: taiga/projects/wiki/models.py:71 taiga/users/models.py:206 +#: taiga/projects/wiki/models.py:72 taiga/users/models.py:206 msgid "order" msgstr "次序" @@ -1386,14 +1387,18 @@ msgstr "自定" msgid "Talky" msgstr "Talky" -#: taiga/projects/custom_attributes/models.py:33 +#: taiga/projects/custom_attributes/choices.py:25 msgid "Text" msgstr "單行文字" -#: taiga/projects/custom_attributes/models.py:34 +#: taiga/projects/custom_attributes/choices.py:26 msgid "Multi-Line Text" msgstr "多行列文字" +#: taiga/projects/custom_attributes/choices.py:27 +msgid "Date" +msgstr "" + #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 msgid "type" @@ -1539,17 +1544,17 @@ msgstr "來自:" msgid "To:" msgstr "給:" -#: taiga/projects/history/templatetags/functions.py:26 -#: taiga/projects/wiki/models.py:32 +#: taiga/projects/history/templatetags/functions.py:24 +#: taiga/projects/wiki/models.py:33 msgid "content" msgstr "內容" -#: taiga/projects/history/templatetags/functions.py:27 +#: taiga/projects/history/templatetags/functions.py:25 #: taiga/projects/mixins/blocked.py:31 msgid "blocked note" msgstr "封鎖筆記" -#: taiga/projects/history/templatetags/functions.py:28 +#: taiga/projects/history/templatetags/functions.py:26 msgid "sprint" msgstr "衝刺任務" @@ -1615,10 +1620,23 @@ msgstr "指派給" msgid "external reference" msgstr "外部參考" +#: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 +msgid "count" +msgstr "" + +#: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 +#: taiga/projects/likes/models.py:56 +msgid "Likes" +msgstr "" + +#: taiga/projects/likes/models.py:55 +msgid "Like" +msgstr "" + #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 #: taiga/projects/models.py:543 taiga/projects/models.py:601 -#: taiga/projects/wiki/models.py:30 taiga/users/models.py:200 +#: taiga/projects/wiki/models.py:31 taiga/users/models.py:200 msgid "slug" msgstr "代稱" @@ -1865,12 +1883,12 @@ msgstr "通知用戶" msgid "Watched" msgstr "" -#: taiga/projects/notifications/services.py:66 -#: taiga/projects/notifications/services.py:80 +#: taiga/projects/notifications/services.py:65 +#: taiga/projects/notifications/services.py:79 msgid "Notify exists for specified user and project" msgstr "通知特定使用者與專案退出" -#: taiga/projects/notifications/services.py:428 +#: taiga/projects/notifications/services.py:427 msgid "Invalid value for notify level" msgstr "" @@ -2619,47 +2637,47 @@ msgstr "版本" msgid "You can't leave the project if there are no more owners" msgstr "如果專案無所有者,你將無法脫離該專案" -#: taiga/projects/serializers.py:242 +#: taiga/projects/serializers.py:240 msgid "Email address is already taken" msgstr "電子郵件已使用" -#: taiga/projects/serializers.py:254 +#: taiga/projects/serializers.py:252 msgid "Invalid role for the project" msgstr "專案無效的角色" -#: taiga/projects/serializers.py:399 +#: taiga/projects/serializers.py:397 msgid "Default options" msgstr "預設選項" -#: taiga/projects/serializers.py:400 +#: taiga/projects/serializers.py:398 msgid "User story's statuses" msgstr "使用者故事狀態" -#: taiga/projects/serializers.py:401 +#: taiga/projects/serializers.py:399 msgid "Points" msgstr "點數" -#: taiga/projects/serializers.py:402 +#: taiga/projects/serializers.py:400 msgid "Task's statuses" msgstr "任務狀態" -#: taiga/projects/serializers.py:403 +#: taiga/projects/serializers.py:401 msgid "Issue's statuses" msgstr "問題狀態" -#: taiga/projects/serializers.py:404 +#: taiga/projects/serializers.py:402 msgid "Issue's types" msgstr "問題類型" -#: taiga/projects/serializers.py:405 +#: taiga/projects/serializers.py:403 msgid "Priorities" msgstr "優先性" -#: taiga/projects/serializers.py:406 +#: taiga/projects/serializers.py:404 msgid "Severities" msgstr "嚴重性" -#: taiga/projects/serializers.py:407 +#: taiga/projects/serializers.py:405 msgid "Roles" msgstr "角色" @@ -3142,10 +3160,6 @@ msgstr "該ID無相關使用者故事狀態" msgid "There's no task status with that id" msgstr "該ID無相關任務狀況" -#: taiga/projects/votes/models.py:28 -msgid "count" -msgstr "" - #: taiga/projects/votes/models.py:31 taiga/projects/votes/models.py:32 #: taiga/projects/votes/models.py:56 msgid "Votes" @@ -3163,11 +3177,11 @@ msgstr "'content'參數為必要" msgid "'project_id' parameter is mandatory" msgstr "'project_id'參數為必要" -#: taiga/projects/wiki/models.py:36 +#: taiga/projects/wiki/models.py:37 msgid "last modifier" msgstr "上次更改" -#: taiga/projects/wiki/models.py:69 +#: taiga/projects/wiki/models.py:70 msgid "href" msgstr "href" @@ -3187,56 +3201,56 @@ msgstr "許可" msgid "Important dates" msgstr "重要日期" -#: taiga/users/api.py:150 taiga/users/api.py:157 -msgid "Invalid username or email" -msgstr "無效使用者或郵件" - -#: taiga/users/api.py:166 -msgid "Mail sended successful!" -msgstr "成功送出郵件" - -#: taiga/users/api.py:178 taiga/users/api.py:183 -msgid "Token is invalid" -msgstr "代號無效" - -#: taiga/users/api.py:204 -msgid "Current password parameter needed" -msgstr "需要目前密碼之參數" - -#: taiga/users/api.py:207 -msgid "New password parameter needed" -msgstr "需要新密碼參數" - -#: taiga/users/api.py:210 -msgid "Invalid password length at least 6 charaters needed" -msgstr "無效密碼長度,至少需6個字元" - -#: taiga/users/api.py:213 -msgid "Invalid current password" -msgstr "無效密碼" - -#: taiga/users/api.py:229 -msgid "Incomplete arguments" -msgstr "不完整參數" - -#: taiga/users/api.py:234 -msgid "Invalid image format" -msgstr "無效的圖片檔案" - -#: taiga/users/api.py:279 +#: taiga/users/api.py:111 msgid "Duplicated email" msgstr "複製電子郵件" -#: taiga/users/api.py:281 +#: taiga/users/api.py:113 msgid "Not valid email" msgstr "非有效電子郵性" -#: taiga/users/api.py:301 taiga/users/api.py:307 +#: taiga/users/api.py:146 taiga/users/api.py:153 +msgid "Invalid username or email" +msgstr "無效使用者或郵件" + +#: taiga/users/api.py:161 +msgid "Mail sended successful!" +msgstr "成功送出郵件" + +#: taiga/users/api.py:173 taiga/users/api.py:178 +msgid "Token is invalid" +msgstr "代號無效" + +#: taiga/users/api.py:199 +msgid "Current password parameter needed" +msgstr "需要目前密碼之參數" + +#: taiga/users/api.py:202 +msgid "New password parameter needed" +msgstr "需要新密碼參數" + +#: taiga/users/api.py:205 +msgid "Invalid password length at least 6 charaters needed" +msgstr "無效密碼長度,至少需6個字元" + +#: taiga/users/api.py:208 +msgid "Invalid current password" +msgstr "無效密碼" + +#: taiga/users/api.py:224 +msgid "Incomplete arguments" +msgstr "不完整參數" + +#: taiga/users/api.py:229 +msgid "Invalid image format" +msgstr "無效的圖片檔案" + +#: taiga/users/api.py:256 taiga/users/api.py:262 msgid "" "Invalid, are you sure the token is correct and you didn't use it before?" msgstr "無效,請確認代號正確,之前是否曾使用過?" -#: taiga/users/api.py:334 taiga/users/api.py:342 taiga/users/api.py:345 +#: taiga/users/api.py:289 taiga/users/api.py:297 taiga/users/api.py:300 msgid "Invalid, are you sure the token is correct?" msgstr "無效,請確認代號是否正確?" From 563088d2bd84a0ef25bc0ce37ca064dbaba39b9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Wed, 28 Oct 2015 10:00:12 +0100 Subject: [PATCH 177/190] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8c423ba..396bd18f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog # -## 1.9.0 ??? (unreleased) +## 1.9.0 Abies Siberica (2015-11-XX) ### Features From cfc72340f8535fda549e67b986dfaa6de65a3ac4 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 28 Oct 2015 14:29:44 +0100 Subject: [PATCH 178/190] Fixing closed points calculation --- taiga/projects/services/stats.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/taiga/projects/services/stats.py b/taiga/projects/services/stats.py index 70d4cc3f..2b4ec4a1 100644 --- a/taiga/projects/services/stats.py +++ b/taiga/projects/services/stats.py @@ -21,6 +21,7 @@ import datetime import copy from taiga.projects.history.models import HistoryEntry +from taiga.projects.userstories.models import RolePoints def _get_total_story_points(project): @@ -225,10 +226,11 @@ def get_stats_for_project(project): points = project.calculated_points - closed_milestone_query = Q(role_points__user_story__milestone__closed=True) - null_milestone_query = Q(role_points__user_story__milestone__isnull=True) - closed_points = sum(project.points.filter(closed_milestone_query|null_milestone_query)\ - .exclude(value__isnull=True).values_list("value", flat=True)) + closed_points = sum(RolePoints.objects.filter(user_story__project=project).filter( + Q(user_story__milestone__closed=True) | + Q(user_story__milestone__isnull=True) + ).exclude(points__value__isnull=True).values_list("points__value", flat=True)) + closed_milestones = project.milestones.filter(closed=True).count() speed = 0 if closed_milestones != 0: From 3b082a1c9b77a20ade1cf2d22cbdc045649bcb10 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 28 Oct 2015 14:35:02 +0100 Subject: [PATCH 179/190] Fixing watchers validators --- taiga/projects/notifications/validators.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taiga/projects/notifications/validators.py b/taiga/projects/notifications/validators.py index a79cb8b7..1330a09a 100644 --- a/taiga/projects/notifications/validators.py +++ b/taiga/projects/notifications/validators.py @@ -40,7 +40,8 @@ class WatchersValidator: # Check if incoming watchers are contained # in project members list member_ids = project.members.values_list("id", flat=True) - result = set(users).difference(member_ids) + existing_watcher_ids = project.get_watchers().values_list("id", flat=True) + result = set(users).difference(member_ids).difference(existing_watcher_ids) if result: raise serializers.ValidationError(_("Watchers contains invalid users")) From 6b7d8e5280a54efca13d9b95310da5a6ee01fd72 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 29 Oct 2015 08:51:28 +0100 Subject: [PATCH 180/190] Fixing sample_data to avoid empty tags on certain issues --- taiga/projects/management/commands/sample_data.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index 1725db2e..f25ba274 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -285,7 +285,7 @@ class Command(BaseCommand): project=project)), type=self.sd.db_object_from_queryset(IssueType.objects.filter( project=project)), - tags=self.sd.words(0, 10).split(" ")) + tags=self.sd.words(1, 10).split(" ")) bug.save() @@ -494,4 +494,3 @@ class Command(BaseCommand): for i in range(self.sd.int(*NUM_WATCHERS)): user = self.sd.db_object_from_queryset(User.objects.all()) obj.add_watcher(user) - From f8060aeef7db3ec22c0a1f4cef7356df12111483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 29 Oct 2015 09:13:00 +0100 Subject: [PATCH 181/190] Update README.md --- README.md | 85 +++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 37aca49a..19d123c3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,79 @@ [![Coverage Status](https://coveralls.io/repos/taigaio/taiga-back/badge.svg?branch=master)](https://coveralls.io/r/taigaio/taiga-back?branch=master "Coverage Status") [![Dependency Status](https://www.versioneye.com/user/projects/561bd091a193340f32001464/badge.svg?style=flat)](https://www.versioneye.com/user/projects/561bd091a193340f32001464) + +## Contribute to Taiga ## + +#### Where to start #### + +There are many different ways to contribute to Taiga's development, just find the one that best fits with your skills. Examples of contributions we would love to receive include: + +- **Code patches** +- **Documentation improvements** +- **Translations** +- **Bug reports** +- **Patch reviews** +- **UI enhancements** + +Big features are also welcome but if you want to see your contributions included in Taiga codebase we strongly recommend you start by initiating a chat though our [mailing list](http://groups.google.co.uk/d/forum/taigaio) + + +#### License #### + +Every code patch accepted in taiga codebase is licensed under [AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html). You should must be careful to not include any code that can not be licensed under this license. + +Please read carefully [our license](https://github.com/taigaio/taiga-back/blob/master/LICENSE) and ask us if you have any questions. + + +#### Bug reports, enanhcements and support #### + +If you **nedd help to setup Taiga**, you want to **talk about some cool enhancemnt** or you have **some questions** please write us to our [mailing list](http://groups.google.com/d/forum/taigaio). + +If you **find a bug** in Taiga you can always report it: + +- in our [mailing list](http://groups.google.com/d/forum/taigaio). +- in [github issues](https://github.com/taigaio/taiga-back/issues). +- send us a mail to support@taiga.io if is a bug related to tree.taiga.io. +- send a mail to security@taiga.io if is a **security bug**. + +One of our fellow Taiga developers will search, find and hunt it as soon as possible. + +Please, before reporting an bug write down how can we reproduce it, your operating system, your browser and version, and if it's possible, a screenshot. Sometimes it take less time to fix a bug if the developer know how to find it and we will solve your problem as fast as possible. + + +#### Documentation improvements #### + +We are gathering lots of information from our users to build and enhance our documentation. If you are the documentation to install or develop with Taiga and find any mistakes, omissions or confused sequences, it is enormously helpful to report it. Or better still, if you believe you can author additions, please make a pull-request to taiga project. + +Currently, we have authored three main documentation hubs: + +- **[API Docs](https://github.com/taigaio/taiga-doc)**: Our API documentation and reference for developing from Taiga API. +- **[Installation Guide](https://github.com/taigaio/taiga-doc)**: If you need to install Taiga on your own server, this is the place to find some guides. +- **[Taiga Support](https://github.com/taigaio/taiga-doc)**: This page is intended to be the support reference page for the users. If you find any mistake, please report it. + + +#### Translation #### + +We are ready now to accept your help translating Taiga. It's easy (and fun!) just access to our team of translators with the link below, set up an account in Transifex and start contributing. Join us to make sure your language is covered! **[Help Taiga to trasnlate content](https://www.transifex.com/signup/ "Help Taiga to trasnlatecontent")** + + +#### Code patches #### + +Taiga will always be glad to receive code patches to update, fix or improve its code. + +If you know how to improve our code base or you found a bug, a security vulnerabilities a performance issue and you think you can solve, we will be very happy to accept your pull-request. If your code requires considerable changes, we recommend you first talk to us directly. We will find the best way to help. + + +#### UI enhancements #### + +Taiga is made for developers and designers. We care enormously about UI because usability and design are both critical aspects of the Taiga experience. + +There are two possible ways to contribute to our UI: +- **Bugs**: If you find a bug regarding front-end, please report it as previously indicated in the Bug reports section or send a pull-request as indicated in the Code Patches section. +- **Enhancements**: If its a design or UX bug or enhancement we will love to receive your feedback. Please send us your enhancement, with the reason and, if it's possible, an example. Our design and UX team will review your enhancement and fix it as soon as possible. We recommend you to use our [mailing list](http://groups.google.co.uk/d/forum/taigaio){target="_blank"} so we can have a lot of different opinions and debate. +- **Language Localization**: We are eager to offer localized versions of Taiga. Some members of the community have already volunteered to work to provide a variety of languages. We are working to implement some changes to allow for this and expect to accept these requests in the near future. + + ## Setup development environment ## Just execute these commands in your virtualenv(wrapper): @@ -25,15 +98,3 @@ Initial auth data: admin/123123 If you want a complete environment for production usage, you can try the taiga bootstrapping scripts https://github.com/taigaio/taiga-scripts (warning: alpha state). All the information about the different installation methods (production, development, vagrant, docker...) can be found here http://taigaio.github.io/taiga-doc/dist/#_installation_guide. - -## Community ## - -[Taiga has a mailing list](http://groups.google.com/d/forum/taigaio). Feel free to join it and ask any questions you may have. - -To subscribe for announcements of releases, important changes and so on, please follow [@taigaio](https://twitter.com/taigaio) on Twitter. - -## Donations ## - -We are grateful for your emails volunteering donations to Taiga. We feel comfortable accepting them under these conditions: The first that we will only do so while we are in the current beta / pre-revenue stage and that whatever money is donated will go towards a bounty fund. Starting Q2 2015 we will be engaging much more actively with our community to help further the development of Taiga, and we will use these donations to reward people working alongside us. - -If you wish to make a donation to this Taiga fund, you can do so via http://www.paypal.com using the email: eposner@taiga.io From 20f400d2abd4cf732580bd9edaa6149118c6514a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 29 Oct 2015 10:18:26 +0100 Subject: [PATCH 182/190] Update requirements.txt --- requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/requirements.txt b/requirements.txt index f5fd9762..533a20e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ django-sampledatahelper==0.3.0 gunicorn==19.3.0 psycopg2==2.6.1 Pillow==3.0.0 -pytz==2015.6 +pytz==2015.7 six==1.10.0 amqp==1.4.7 djmail==0.11 @@ -15,13 +15,13 @@ django-jinja==1.4.1 jinja2==2.8 pygments==2.0.2 django-sites==0.8 -Markdown==2.6.2 +Markdown==2.6.3 fn==0.4.3 diff-match-patch==20121119 requests==2.8.1 django-sr==0.0.4 easy-thumbnails==2.2 -celery==3.1.18 +celery==3.1.19 redis==2.10.3 Unidecode==0.04.18 raven==5.8.1 @@ -32,4 +32,4 @@ cssutils==1.0.1 # Compatible with python 3.5 django-transactional-cleanup==0.1.15 lxml==3.5.0b1 git+https://github.com/Xof/django-pglocks.git@dbb8d7375066859f897604132bd437832d2014ea -pyjwkest==1.0.6 +pyjwkest==1.0.7 From 431500e730026d7710f017fa9718fedec92b920e Mon Sep 17 00:00:00 2001 From: Max Hellwig Date: Thu, 29 Oct 2015 11:34:52 +0100 Subject: [PATCH 183/190] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 19d123c3..5aa5093e 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Every code patch accepted in taiga codebase is licensed under [AGPL v3.0](http:/ Please read carefully [our license](https://github.com/taigaio/taiga-back/blob/master/LICENSE) and ask us if you have any questions. -#### Bug reports, enanhcements and support #### +#### Bug reports, enhancements and support #### If you **nedd help to setup Taiga**, you want to **talk about some cool enhancemnt** or you have **some questions** please write us to our [mailing list](http://groups.google.com/d/forum/taigaio). From b5af26cf35ffd486e28170af62d68d714aeab8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 29 Oct 2015 15:01:55 +0100 Subject: [PATCH 184/190] Add some comments --- taiga/hooks/bitbucket/services.py | 1 + taiga/hooks/github/services.py | 1 + taiga/hooks/gitlab/services.py | 1 + 3 files changed, 3 insertions(+) diff --git a/taiga/hooks/bitbucket/services.py b/taiga/hooks/bitbucket/services.py index 2bb9e574..ff39d083 100644 --- a/taiga/hooks/bitbucket/services.py +++ b/taiga/hooks/bitbucket/services.py @@ -23,6 +23,7 @@ from taiga.users.models import User from taiga.base.utils.urls import get_absolute_url +# Set this in settings.PROJECT_MODULES_CONFIGURATORS["bitbucket"] def get_or_generate_config(project): config = project.modules_config.config if config and "bitbucket" in config: diff --git a/taiga/hooks/github/services.py b/taiga/hooks/github/services.py index cbd63ef6..31a4f063 100644 --- a/taiga/hooks/github/services.py +++ b/taiga/hooks/github/services.py @@ -23,6 +23,7 @@ from taiga.users.models import AuthData from taiga.base.utils.urls import get_absolute_url +# Set this in settings.PROJECT_MODULES_CONFIGURATORS["github"] def get_or_generate_config(project): config = project.modules_config.config if config and "github" in config: diff --git a/taiga/hooks/gitlab/services.py b/taiga/hooks/gitlab/services.py index c7bfe5b9..a441c0dc 100644 --- a/taiga/hooks/gitlab/services.py +++ b/taiga/hooks/gitlab/services.py @@ -23,6 +23,7 @@ from taiga.users.models import User from taiga.base.utils.urls import get_absolute_url +# Set this in settings.PROJECT_MODULES_CONFIGURATORS["gitlab"] def get_or_generate_config(project): config = project.modules_config.config if config and "gitlab" in config: From efb86b775df4218ffa7035f421e6431beccc47e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 29 Oct 2015 19:07:18 +0100 Subject: [PATCH 185/190] Fix issue #3373 Incorrect link in auto-generated issue->US promoting comment --- taiga/locale/en/LC_MESSAGES/django.po | 14 ++++++-------- taiga/projects/userstories/api.py | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 192c9781..be03627f 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" +"POT-Creation-Date: 2015-10-29 19:06+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" @@ -1319,7 +1319,7 @@ msgstr "" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "" @@ -2185,7 +2185,7 @@ msgid "" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" msgstr "" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "" @@ -2253,11 +2253,11 @@ msgstr "" msgid "Roles" msgstr "" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "" @@ -2639,9 +2639,7 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" #: taiga/projects/userstories/models.py:37 diff --git a/taiga/projects/userstories/api.py b/taiga/projects/userstories/api.py index 710e4461..66c7b521 100644 --- a/taiga/projects/userstories/api.py +++ b/taiga/projects/userstories/api.py @@ -251,8 +251,7 @@ class UserStoryViewSet(OCCResourceMixin, VotedResourceMixin, HistoryResourceMixi if response.status_code == status.HTTP_201_CREATED and self.object.generated_from_issue: self.object.generated_from_issue.save() - comment = _("Generating the user story [US #{ref} - " - "{subject}](:us:{ref} \"US #{ref} - {subject}\")") + comment = _("Generating the user story #{ref} - {subject}") comment = comment.format(ref=self.object.ref, subject=self.object.subject) history = take_snapshot(self.object.generated_from_issue, comment=comment, From b2ce7667c88e03ea358c26389b257744bae9071d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 30 Oct 2015 14:47:55 +0100 Subject: [PATCH 186/190] Fixing calculated tags for watched/voted/liked content --- taiga/users/serializers.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/taiga/users/serializers.py b/taiga/users/serializers.py index 2a7000fd..759e5a0f 100644 --- a/taiga/users/serializers.py +++ b/taiga/users/serializers.py @@ -236,7 +236,10 @@ class HighLightedContentSerializer(serializers.Serializer): def get_tags_color(self, obj): tags = obj.get("tags", []) - return [{"name": tc[0], "color": tc[1]} for tc in obj.get("tags_colors", []) if tc[0] in tags] + tags = tags if tags is not None else [] + tags_colors = obj.get("tags_colors", []) + tags_colors = tags_colors if tags_colors is not None else [] + return [{"name": tc[0], "color": tc[1]} for tc in tags_colors if tc[0] in tags] def get_is_watcher(self, obj): return obj["id"] in self.user_watching.get(obj["type"], []) From 5c8c20b2891cd8cff85a33ae1175b67550da2ad4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Fri, 30 Oct 2015 18:20:52 +0100 Subject: [PATCH 187/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/de/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/en/LC_MESSAGES/django.po | 2 +- taiga/locale/es/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/fi/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/fr/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/it/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/nl/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/pl/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/pt_BR/LC_MESSAGES/django.po | 30 ++++++++++------------ taiga/locale/ru/LC_MESSAGES/django.po | 24 ++++++++--------- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 23 ++++++++--------- 12 files changed, 114 insertions(+), 157 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 4e60447a..48915f80 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" @@ -1346,7 +1346,7 @@ msgstr "Borrat" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Sense assignar" @@ -1701,15 +1701,15 @@ msgstr "rols" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "No observant" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Observant" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignorant" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2218,7 +2218,7 @@ msgstr "" "\n" "[%(project)s] Borrada pàgina de Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "" @@ -2286,11 +2286,11 @@ msgstr "Severitats" msgid "Roles" msgstr "Rols" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "" @@ -2690,12 +2690,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Generant l'història d'usuari [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index c2ee143b..715adb19 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -15,8 +15,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" @@ -1516,7 +1516,7 @@ msgstr "entfernt" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Nicht zugewiesen" @@ -1875,15 +1875,15 @@ msgstr "Rollen" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Nicht beobachten" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Beobachten" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignorieren" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2670,7 +2670,7 @@ msgstr "" "[%(project)s] löschte die Wiki Seite \"%(page)s\"\n" "\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Beobachter enthält ungültige Benutzer " @@ -2740,11 +2740,11 @@ msgstr "Gewichtung" msgid "Roles" msgstr "Rollen" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Zukünftiger Sprint" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Projektende" @@ -3169,12 +3169,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Erstelle die User-Story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index be03627f..c90059a1 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-29 19:06+0100\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 8c3e81ba..7191d97d 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" @@ -1512,7 +1512,7 @@ msgstr "borrado" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "No asignado" @@ -1869,15 +1869,15 @@ msgstr "roles" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "No observado" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Observado" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignorado" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2617,7 +2617,7 @@ msgstr "" "\n" "[%(project)s] Borrada la página del wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Los observadores tienen usuarios invalidos" @@ -2686,11 +2686,11 @@ msgstr "Gravedades" msgid "Roles" msgstr "Roles" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Sprint futuro" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Final de proyecto" @@ -3126,12 +3126,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Generada la historia de usuario [US #{ref} - {subject}](:us:{ref} \"US " -"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 1323f434..8f560281 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" @@ -1475,7 +1475,7 @@ msgstr "poistettu" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Tekijä puuttuu" @@ -1830,15 +1830,15 @@ msgstr "roolit" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Ei seuraa" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Seuraa" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ohittaa" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2587,7 +2587,7 @@ msgstr "" "\n" "[%(project)s] Poistettiin wiki-sivu \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Vahdit sisältävät virheellisiä käyttäjiä" @@ -2655,11 +2655,11 @@ msgstr "Vakavuudet" msgid "Roles" msgstr "Roolit" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Tuleva kierros" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Projektin loppu" @@ -3090,12 +3090,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Luodaan käyttäjätarinaa [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 90130ef3..1eb2d792 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -17,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" @@ -1447,7 +1447,7 @@ msgstr "supprimé" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Non assigné" @@ -1802,15 +1802,15 @@ msgstr "rôles" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Non surveillé" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Sous surveillance" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignoré" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2331,7 +2331,7 @@ msgstr "" "\n" "[%(project)s] Page Wiki \"%(page)s\" supprimée\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "La liste des observateurs contient des utilisateurs invalides" @@ -2400,11 +2400,11 @@ msgstr "Sévérités" msgid "Roles" msgstr "Rôles" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Sprint futurs" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Fin du projet" @@ -2823,12 +2823,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Génération de l'histoire utilisateur [HU #{ref} - {subject}](:us:{ref} \"HU " -"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index 5ce9ea7b..770058e4 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -13,8 +13,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/it/)\n" @@ -1608,7 +1608,7 @@ msgstr "rimosso" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Non assegnato" @@ -1964,15 +1964,15 @@ msgstr "ruoli" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Non in osservazione" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "In osservazione" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignorato" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2861,7 +2861,7 @@ msgstr "" "\n" "[%(project)s] ha eliminato la pagina wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "L'osservatore contiene un utente non valido" @@ -2929,11 +2929,11 @@ msgstr "Criticità" msgid "Roles" msgstr "Ruoli" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Sprint futuri" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Termine di progetto" @@ -3387,12 +3387,8 @@ msgstr "Non hai i permessi per aggiungere questo stato a questa storia utente." #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Stiamo generando la storia utente [US #{ref} - {subject}](:us:{ref} \"US " -"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index a5236317..f73e35e9 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" @@ -1409,7 +1409,7 @@ msgstr "verwijderd" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Niet toegewezen" @@ -1766,15 +1766,15 @@ msgstr "rollen" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Niet volgend" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Volgend" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Negerend" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2307,7 +2307,7 @@ msgstr "" "\n" "[%(project)s] Wiki Pagina verwijderd \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Volgers bevat ongeldige gebruikers" @@ -2375,11 +2375,11 @@ msgstr "Ernstniveaus" msgid "Roles" msgstr "Rollen" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Toekomstige sprint" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Project einde" @@ -2785,12 +2785,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"User story wordt gegenereerd [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index ec0ecbfb..47dba98d 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" @@ -1517,7 +1517,7 @@ msgstr "usuniete" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Nieprzypisane" @@ -1872,15 +1872,15 @@ msgstr "role" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Nie obserwujesz" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Obserwujesz" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Ignorujesz" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2644,7 +2644,7 @@ msgstr "" "\n" "[%(project)s] Usunął stronę Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Obserwatorzy zawierają niepoprawnych użytkowników" @@ -2712,11 +2712,11 @@ msgstr "Ważność" msgid "Roles" msgstr "Role" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Przyszły sprint" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Zakończenie projektu" @@ -3152,12 +3152,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Generowanie historyjki użytkownika [HU #{ref} - {subject}](:us:{ref} \"HU " -"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 6dea9fca..7cadff84 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -18,8 +18,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" @@ -1422,7 +1422,7 @@ msgstr "Multi-linha" #: taiga/projects/custom_attributes/choices.py:27 msgid "Date" -msgstr "" +msgstr "Data" #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 @@ -1522,7 +1522,7 @@ msgstr "removido" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Não-atribuído" @@ -1652,11 +1652,11 @@ msgstr "contagem" #: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 #: taiga/projects/likes/models.py:56 msgid "Likes" -msgstr "" +msgstr "Curtidas" #: taiga/projects/likes/models.py:55 msgid "Like" -msgstr "" +msgstr "Curtir" #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 @@ -1877,15 +1877,15 @@ msgstr "funções" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "não observando" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "observando" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "ignorando" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2627,7 +2627,7 @@ msgstr "" "\n" "[%(project)s] Apagou a página Wiki \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "Observadores contém usuários inválidos" @@ -2695,11 +2695,11 @@ msgstr "Severidades" msgid "Roles" msgstr "Funções" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Sprint futuro" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Fim do projeto" @@ -3132,12 +3132,8 @@ msgstr "Você não tem permissão para colocar esse status para essa user story. #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Gerando a user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 47089590..5c1ff7c4 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" @@ -1522,7 +1522,7 @@ msgstr "удалено" #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "Не назначено" @@ -1883,15 +1883,15 @@ msgstr "роли" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "Не отслеживаемое" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "Отслеживаемое" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "Игнорируется" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2641,7 +2641,7 @@ msgstr "" "\n" "[%(project)s] Удалена вики-страница \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "наблюдатели содержат неправильных пользователей" @@ -2709,11 +2709,11 @@ msgstr "Степени важности" msgid "Roles" msgstr "Роли" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "Будущий спринт" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "Окончание проекта" @@ -3152,12 +3152,8 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"Генерируется пользовательская история [US #{ref} - {subject}](:us:{ref} \"US " -"#{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index 3ad1b75d..e5ae704f 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-27 09:53+0100\n" -"PO-Revision-Date: 2015-10-27 08:53+0000\n" +"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" @@ -1497,7 +1497,7 @@ msgstr "移除 " #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 -#: taiga/projects/services/stats.py:137 taiga/projects/services/stats.py:138 +#: taiga/projects/services/stats.py:138 taiga/projects/services/stats.py:139 msgid "Unassigned" msgstr "無指定" @@ -1852,15 +1852,15 @@ msgstr "角色" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "不觀看" +msgstr "" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "觀看中" +msgstr "" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" -msgstr "忽視" +msgstr "" #: taiga/projects/notifications/models.py:61 msgid "created date time" @@ -2613,7 +2613,7 @@ msgstr "" "\n" "[%(project)s] 刪除維基頁 \"%(page)s\"\n" -#: taiga/projects/notifications/validators.py:45 +#: taiga/projects/notifications/validators.py:46 msgid "Watchers contains invalid users" msgstr "監督者包含無效使用者" @@ -2681,11 +2681,11 @@ msgstr "嚴重性" msgid "Roles" msgstr "角色" -#: taiga/projects/services/stats.py:84 +#: taiga/projects/services/stats.py:85 msgid "Future sprint" msgstr "未來之衝刺" -#: taiga/projects/services/stats.py:101 +#: taiga/projects/services/stats.py:102 msgid "Project End" msgstr "專案結束" @@ -3109,11 +3109,8 @@ msgstr "無權限更動此使用者故事的狀態" #: taiga/projects/userstories/api.py:254 #, python-brace-format -msgid "" -"Generating the user story [US #{ref} - {subject}](:us:{ref} \"US #{ref} - " -"{subject}\")" +msgid "Generating the user story #{ref} - {subject}" msgstr "" -"産生使用者故事[US #{ref} - {subject}](:us:{ref} \"US #{ref} - {subject}\")" #: taiga/projects/userstories/models.py:37 msgid "role" From 037b098e577c9c698f7a282c22fe35cee3f504b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 2 Nov 2015 09:11:26 +0100 Subject: [PATCH 188/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 2 +- taiga/locale/de/LC_MESSAGES/django.po | 33 +++++++++++----------- taiga/locale/en/LC_MESSAGES/django.po | 2 +- taiga/locale/es/LC_MESSAGES/django.po | 14 ++++----- taiga/locale/fi/LC_MESSAGES/django.po | 2 +- taiga/locale/fr/LC_MESSAGES/django.po | 2 +- taiga/locale/it/LC_MESSAGES/django.po | 2 +- taiga/locale/nl/LC_MESSAGES/django.po | 2 +- taiga/locale/pl/LC_MESSAGES/django.po | 2 +- taiga/locale/pt_BR/LC_MESSAGES/django.po | 2 +- taiga/locale/ru/LC_MESSAGES/django.po | 2 +- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 2 +- 12 files changed, 34 insertions(+), 33 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 48915f80..88083595 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index 715adb19..b754309b 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -10,14 +10,15 @@ # Henning Matthaei, 2015 # Regina , 2015 # Sebastian Blum , 2015 +# Silsha Fux , 2015 # Thomas McWork , 2015 msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" +"PO-Revision-Date: 2015-10-31 01:42+0000\n" +"Last-Translator: Silsha Fux \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -862,11 +863,11 @@ msgstr "Name" #: taiga/external_apps/models.py:35 msgid "Icon url" -msgstr "" +msgstr "Icon URL" #: taiga/external_apps/models.py:36 msgid "web" -msgstr "" +msgstr "Web" #: taiga/external_apps/models.py:37 taiga/projects/attachments/models.py:74 #: taiga/projects/custom_attributes/models.py:35 @@ -879,7 +880,7 @@ msgstr "Beschreibung" #: taiga/external_apps/models.py:39 msgid "Next url" -msgstr "" +msgstr "Nächste URL" #: taiga/external_apps/models.py:41 msgid "secret key for ciphering the application tokens" @@ -888,11 +889,11 @@ msgstr "" #: taiga/external_apps/models.py:55 taiga/projects/likes/models.py:50 #: taiga/projects/notifications/models.py:84 taiga/projects/votes/models.py:50 msgid "user" -msgstr "" +msgstr "Benutzer" #: taiga/external_apps/models.py:59 msgid "application" -msgstr "" +msgstr "Applikation" #: taiga/feedback/models.py:23 taiga/users/models.py:113 msgid "full name" @@ -1374,7 +1375,7 @@ msgstr "Angehangene Datei" #: taiga/projects/attachments/models.py:71 msgid "sha1" -msgstr "" +msgstr "SHA1" #: taiga/projects/attachments/models.py:73 msgid "is deprecated" @@ -1416,7 +1417,7 @@ msgstr "Mehrzeiliger Text" #: taiga/projects/custom_attributes/choices.py:27 msgid "Date" -msgstr "" +msgstr "Datum" #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 @@ -1645,16 +1646,16 @@ msgstr "externe Referenz" #: taiga/projects/likes/models.py:28 taiga/projects/votes/models.py:28 msgid "count" -msgstr "" +msgstr "Count" #: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 #: taiga/projects/likes/models.py:56 msgid "Likes" -msgstr "" +msgstr "Likes" #: taiga/projects/likes/models.py:55 msgid "Like" -msgstr "" +msgstr "Like" #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 @@ -1875,11 +1876,11 @@ msgstr "Rollen" #: taiga/projects/notifications/choices.py:28 msgid "Not watching" -msgstr "" +msgstr "Nicht beobachtend" #: taiga/projects/notifications/choices.py:29 msgid "Watching" -msgstr "" +msgstr "Beobachtend" #: taiga/projects/notifications/choices.py:30 msgid "Ignoring" @@ -1904,7 +1905,7 @@ msgstr "Benutzer benachrichtigen" #: taiga/projects/notifications/models.py:90 #: taiga/projects/notifications/models.py:91 msgid "Watched" -msgstr "" +msgstr "Beobachtet" #: taiga/projects/notifications/services.py:65 #: taiga/projects/notifications/services.py:79 diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index c90059a1..38e42ee3 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index 7191d97d..af675f2f 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" -"Last-Translator: Taiga Dev Team \n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" +"PO-Revision-Date: 2015-10-30 17:28+0000\n" +"Last-Translator: David Barragán \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" @@ -1412,7 +1412,7 @@ msgstr "Texto multilínea" #: taiga/projects/custom_attributes/choices.py:27 msgid "Date" -msgstr "" +msgstr "Fecha" #: taiga/projects/custom_attributes/models.py:38 #: taiga/projects/issues/models.py:46 @@ -1642,11 +1642,11 @@ msgstr "recuento" #: taiga/projects/likes/models.py:31 taiga/projects/likes/models.py:32 #: taiga/projects/likes/models.py:56 msgid "Likes" -msgstr "" +msgstr "Likes" #: taiga/projects/likes/models.py:55 msgid "Like" -msgstr "" +msgstr "Like" #: taiga/projects/milestones/models.py:37 taiga/projects/models.py:136 #: taiga/projects/models.py:396 taiga/projects/models.py:460 @@ -3127,7 +3127,7 @@ msgstr "" #: taiga/projects/userstories/api.py:254 #, python-brace-format msgid "Generating the user story #{ref} - {subject}" -msgstr "" +msgstr "Generada la historia de usuario #{ref} - {subject}" #: taiga/projects/userstories/models.py:37 msgid "role" diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 8f560281..1a8e4f83 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 1eb2d792..65e6a717 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -17,7 +17,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index 770058e4..4dda1293 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -13,7 +13,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index f73e35e9..0d4eb446 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 47dba98d..08806b3f 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -10,7 +10,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index 7cadff84..d1897d3a 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -18,7 +18,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 5c1ff7c4..8c4e2604 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -12,7 +12,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index e5ae704f..a08a2337 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-10-30 18:20+0100\n" +"POT-Creation-Date: 2015-11-02 09:09+0100\n" "PO-Revision-Date: 2015-10-30 17:20+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" From d573b8ab184682c3f082cf5ad59ac42aef8f8ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Thu, 29 Oct 2015 11:50:38 +0100 Subject: [PATCH 189/190] Fix notification level names --- taiga/locale/en/LC_MESSAGES/django.po | 6 +-- taiga/projects/api.py | 2 +- .../management/commands/sample_data.py | 12 +++-- taiga/projects/models.py | 4 +- taiga/projects/notifications/api.py | 2 +- taiga/projects/notifications/choices.py | 12 ++--- taiga/projects/notifications/services.py | 16 +++--- taiga/projects/notifications/utils.py | 6 +-- taiga/users/services.py | 10 ++-- tests/integration/test_hooks_bitbucket.py | 2 +- tests/integration/test_hooks_github.py | 2 +- tests/integration/test_hooks_gitlab.py | 2 +- tests/integration/test_notifications.py | 52 +++++++++---------- tests/integration/test_users.py | 2 +- 14 files changed, 68 insertions(+), 62 deletions(-) diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 38e42ee3..13f273ce 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -1673,15 +1673,15 @@ msgid "roles" msgstr "" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/projects/api.py b/taiga/projects/api.py index 4b3c47aa..ceebb2f7 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -80,7 +80,7 @@ class ProjectViewSet(LikedResourceMixin, HistoryResourceMixin, ModelCrudViewSet) def watch(self, request, pk=None): project = self.get_object() self.check_permissions(request, "watch", project) - notify_level = request.DATA.get("notify_level", NotifyLevel.watch) + notify_level = request.DATA.get("notify_level", NotifyLevel.all) project.add_watcher(self.request.user, notify_level=notify_level) return response.Ok() diff --git a/taiga/projects/management/commands/sample_data.py b/taiga/projects/management/commands/sample_data.py index f25ba274..8cc587a0 100644 --- a/taiga/projects/management/commands/sample_data.py +++ b/taiga/projects/management/commands/sample_data.py @@ -29,6 +29,8 @@ from taiga.users.models import * from taiga.permissions.permissions import ANON_PERMISSIONS from taiga.projects.models import * from taiga.projects.milestones.models import * +from taiga.projects.notifications.choices import NotifyLevel + from taiga.projects.userstories.models import * from taiga.projects.tasks.models import * from taiga.projects.issues.models import * @@ -459,7 +461,7 @@ class Command(BaseCommand): take_snapshot(project, user=project.owner) self.create_likes(project) - self.create_watchers(project) + self.create_watchers(project, NotifyLevel.involved) return project @@ -490,7 +492,11 @@ class Command(BaseCommand): user=self.sd.db_object_from_queryset(User.objects.all()) add_like(obj, user) - def create_watchers(self, obj): + def create_watchers(self, obj, notify_level=None): for i in range(self.sd.int(*NUM_WATCHERS)): user = self.sd.db_object_from_queryset(User.objects.all()) - obj.add_watcher(user) + if not notify_level: + obj.add_watcher(user) + else: + obj.add_watcher(user, notify_level) + diff --git a/taiga/projects/models.py b/taiga/projects/models.py index c0455e4b..1bc8ab4d 100644 --- a/taiga/projects/models.py +++ b/taiga/projects/models.py @@ -344,7 +344,7 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): } def _get_q_watchers(self): - return Q(notify_policies__project_id=self.id) & ~Q(notify_policies__notify_level=NotifyLevel.ignore) + return Q(notify_policies__project_id=self.id) & ~Q(notify_policies__notify_level=NotifyLevel.none) def get_watchers(self): return get_user_model().objects.filter(self._get_q_watchers()) @@ -368,7 +368,7 @@ class Project(ProjectDefaults, TaggedMixin, models.Model): related_people = related_people.distinct() return related_people - def add_watcher(self, user, notify_level=NotifyLevel.watch): + def add_watcher(self, user, notify_level=NotifyLevel.all): notify_policy = create_notify_policy_if_not_exists(self, user) set_notify_policy_level(notify_policy, notify_level) diff --git a/taiga/projects/notifications/api.py b/taiga/projects/notifications/api.py index 0d497be4..26b8440f 100644 --- a/taiga/projects/notifications/api.py +++ b/taiga/projects/notifications/api.py @@ -39,7 +39,7 @@ class NotifyPolicyViewSet(ModelCrudViewSet): ).distinct() for project in projects: - services.create_notify_policy_if_not_exists(project, self.request.user, NotifyLevel.watch) + services.create_notify_policy_if_not_exists(project, self.request.user, NotifyLevel.all) def get_queryset(self): if self.request.user.is_anonymous(): diff --git a/taiga/projects/notifications/choices.py b/taiga/projects/notifications/choices.py index 651d5909..e42d9c91 100644 --- a/taiga/projects/notifications/choices.py +++ b/taiga/projects/notifications/choices.py @@ -19,13 +19,13 @@ from django.utils.translation import ugettext_lazy as _ class NotifyLevel(enum.IntEnum): - notwatch = 1 - watch = 2 - ignore = 3 + involved = 1 + all = 2 + none = 3 NOTIFY_LEVEL_CHOICES = ( - (NotifyLevel.notwatch, _("Not watching")), - (NotifyLevel.watch, _("Watching")), - (NotifyLevel.ignore, _("Ignoring")), + (NotifyLevel.involved, _("Involved")), + (NotifyLevel.all, _("All")), + (NotifyLevel.none, _("None")), ) diff --git a/taiga/projects/notifications/services.py b/taiga/projects/notifications/services.py index be6dc949..2be1c8fc 100644 --- a/taiga/projects/notifications/services.py +++ b/taiga/projects/notifications/services.py @@ -52,7 +52,7 @@ def notify_policy_exists(project, user) -> bool: return qs.exists() -def create_notify_policy(project, user, level=NotifyLevel.notwatch): +def create_notify_policy(project, user, level=NotifyLevel.involved): """ Given a project and user, create notification policy for it. """ @@ -65,7 +65,7 @@ def create_notify_policy(project, user, level=NotifyLevel.notwatch): raise exc.IntegrityError(_("Notify exists for specified user and project")) from e -def create_notify_policy_if_not_exists(project, user, level=NotifyLevel.notwatch): +def create_notify_policy_if_not_exists(project, user, level=NotifyLevel.involved): """ Given a project and user, create notification policy for it. """ @@ -85,7 +85,7 @@ def get_notify_policy(project, user): """ model_cls = apps.get_model("notifications", "NotifyPolicy") instance, _ = model_cls.objects.get_or_create(project=project, user=user, - defaults={"notify_level": NotifyLevel.notwatch}) + defaults={"notify_level": NotifyLevel.involved}) return instance @@ -154,9 +154,9 @@ def get_users_to_notify(obj, *, discard_users=None) -> list: return policy.notify_level in [int(x) for x in levels] _can_notify_hard = partial(_check_level, project, - levels=[NotifyLevel.watch]) + levels=[NotifyLevel.all]) _can_notify_light = partial(_check_level, project, - levels=[NotifyLevel.watch, NotifyLevel.notwatch]) + levels=[NotifyLevel.all, NotifyLevel.involved]) candidates = set() candidates.update(filter(_can_notify_hard, project.members.all())) @@ -381,7 +381,7 @@ def get_projects_watched(user_or_id): user_id = user_or_id project_class = apps.get_model("projects", "Project") - return project_class.objects.filter(notify_policies__user__id=user_id).exclude(notify_policies__notify_level=NotifyLevel.ignore) + return project_class.objects.filter(notify_policies__user__id=user_id).exclude(notify_policies__notify_level=NotifyLevel.none) def add_watcher(obj, user): """Add a watcher to an object. @@ -397,7 +397,7 @@ def add_watcher(obj, user): object_id=obj.id, user=user, project=obj.project) notify_policy, _ = apps.get_model("notifications", "NotifyPolicy").objects.get_or_create( - project=obj.project, user=user, defaults={"notify_level": NotifyLevel.watch}) + project=obj.project, user=user, defaults={"notify_level": NotifyLevel.all}) return watched @@ -434,7 +434,7 @@ def set_notify_policy_level_to_ignore(notify_policy): """ Set notification level for specified policy. """ - set_notify_policy_level(notify_policy, NotifyLevel.ignore) + set_notify_policy_level(notify_policy, NotifyLevel.none) def make_ms_thread_index(msg_id, dt): diff --git a/taiga/projects/notifications/utils.py b/taiga/projects/notifications/utils.py index 3df08851..49aea78d 100644 --- a/taiga/projects/notifications/utils.py +++ b/taiga/projects/notifications/utils.py @@ -104,7 +104,7 @@ def attach_project_is_watcher_to_queryset(queryset, user, as_field="is_watcher") THEN TRUE ELSE FALSE END""") - sql = sql.format(tbl=model._meta.db_table, user_id=user.id, ignore_notify_level=NotifyLevel.ignore) + sql = sql.format(tbl=model._meta.db_table, user_id=user.id, ignore_notify_level=NotifyLevel.none) qs = queryset.extra(select={as_field: sql}) return qs @@ -124,7 +124,7 @@ def attach_project_total_watchers_attrs_to_queryset(queryset, as_field="total_wa FROM notifications_notifypolicy WHERE notifications_notifypolicy.project_id = {tbl}.id AND notifications_notifypolicy.notify_level != {ignore_notify_level}""") - sql = sql.format(tbl=model._meta.db_table, ignore_notify_level=NotifyLevel.ignore) + sql = sql.format(tbl=model._meta.db_table, ignore_notify_level=NotifyLevel.none) qs = queryset.extra(select={as_field: sql}) return qs @@ -142,7 +142,7 @@ def attach_notify_level_to_project_queryset(queryset, user): :return: Queryset object with the additional `as_field` field. """ user_id = getattr(user, "id", None) or "NULL" - default_level = NotifyLevel.notwatch + default_level = NotifyLevel.involved sql = strip_lines(""" COALESCE((SELECT notifications_notifypolicy.notify_level diff --git a/taiga/users/services.py b/taiga/users/services.py index aea50ca4..93707737 100644 --- a/taiga/users/services.py +++ b/taiga/users/services.py @@ -218,7 +218,7 @@ def _build_watched_sql_for_projects(for_user): ON (projects_project.id = notifications_notifypolicy.project_id) LEFT JOIN (SELECT project_id, count(*) watchers FROM notifications_notifypolicy - WHERE notifications_notifypolicy.notify_level != {ignore_notify_level} + WHERE notifications_notifypolicy.notify_level != {none_notify_level} GROUP BY project_id ) type_watchers ON projects_project.id = type_watchers.project_id @@ -226,11 +226,11 @@ def _build_watched_sql_for_projects(for_user): ON (projects_project.id = likes_likes.object_id AND {project_content_type_id} = likes_likes.content_type_id) WHERE notifications_notifypolicy.user_id = {for_user_id} - AND notifications_notifypolicy.notify_level != {ignore_notify_level} + AND notifications_notifypolicy.notify_level != {none_notify_level} """ sql = sql.format( for_user_id=for_user.id, - ignore_notify_level=NotifyLevel.ignore, + none_notify_level=NotifyLevel.none, project_content_type_id=ContentType.objects.get(app_label="projects", model="project").id) return sql @@ -248,7 +248,7 @@ def _build_liked_sql_for_projects(for_user): ON (projects_project.id = likes_like.object_id) LEFT JOIN (SELECT project_id, count(*) watchers FROM notifications_notifypolicy - WHERE notifications_notifypolicy.notify_level != {ignore_notify_level} + WHERE notifications_notifypolicy.notify_level != {none_notify_level} GROUP BY project_id ) type_watchers ON projects_project.id = type_watchers.project_id @@ -258,7 +258,7 @@ def _build_liked_sql_for_projects(for_user): """ sql = sql.format( for_user_id=for_user.id, - ignore_notify_level=NotifyLevel.ignore, + none_notify_level=NotifyLevel.none, project_content_type_id=ContentType.objects.get(app_label="projects", model="project").id) return sql diff --git a/tests/integration/test_hooks_bitbucket.py b/tests/integration/test_hooks_bitbucket.py index 27cf34db..bef9330f 100644 --- a/tests/integration/test_hooks_bitbucket.py +++ b/tests/integration/test_hooks_bitbucket.py @@ -265,7 +265,7 @@ def test_issues_event_opened_issue(client): issue.project.save() Membership.objects.create(user=issue.owner, project=issue.project, role=f.RoleFactory.create(project=issue.project), is_owner=True) notify_policy = NotifyPolicy.objects.get(user=issue.owner, project=issue.project) - notify_policy.notify_level = NotifyLevel.watch + notify_policy.notify_level = NotifyLevel.all notify_policy.save() payload = { diff --git a/tests/integration/test_hooks_github.py b/tests/integration/test_hooks_github.py index 7017620f..5b832643 100644 --- a/tests/integration/test_hooks_github.py +++ b/tests/integration/test_hooks_github.py @@ -243,7 +243,7 @@ def test_issues_event_opened_issue(client): issue.project.save() Membership.objects.create(user=issue.owner, project=issue.project, role=f.RoleFactory.create(project=issue.project), is_owner=True) notify_policy = NotifyPolicy.objects.get(user=issue.owner, project=issue.project) - notify_policy.notify_level = NotifyLevel.watch + notify_policy.notify_level = NotifyLevel.all notify_policy.save() payload = { diff --git a/tests/integration/test_hooks_gitlab.py b/tests/integration/test_hooks_gitlab.py index eac03668..7264b2c1 100644 --- a/tests/integration/test_hooks_gitlab.py +++ b/tests/integration/test_hooks_gitlab.py @@ -309,7 +309,7 @@ def test_issues_event_opened_issue(client): issue.project.save() Membership.objects.create(user=issue.owner, project=issue.project, role=f.RoleFactory.create(project=issue.project), is_owner=True) notify_policy = NotifyPolicy.objects.get(user=issue.owner, project=issue.project) - notify_policy.notify_level = NotifyLevel.watch + notify_policy.notify_level = NotifyLevel.all notify_policy.save() payload = { diff --git a/tests/integration/test_notifications.py b/tests/integration/test_notifications.py index 4fafc80b..3173874f 100644 --- a/tests/integration/test_notifications.py +++ b/tests/integration/test_notifications.py @@ -60,14 +60,14 @@ def test_attach_notify_level_to_project_queryset(): qs = utils.attach_notify_level_to_project_queryset(qs, project1.owner) assert len(qs) == 2 - assert qs[0].notify_level == NotifyLevel.notwatch - assert qs[1].notify_level == NotifyLevel.notwatch + assert qs[0].notify_level == NotifyLevel.involved + assert qs[1].notify_level == NotifyLevel.involved - services.create_notify_policy(project1, project1.owner, NotifyLevel.watch) + services.create_notify_policy(project1, project1.owner, NotifyLevel.all) qs = project1.__class__.objects.order_by("id") qs = utils.attach_notify_level_to_project_queryset(qs, project1.owner) - assert qs[0].notify_level == NotifyLevel.watch - assert qs[1].notify_level == NotifyLevel.notwatch + assert qs[0].notify_level == NotifyLevel.all + assert qs[1].notify_level == NotifyLevel.involved def test_create_retrieve_notify_policy(): @@ -81,14 +81,14 @@ def test_create_retrieve_notify_policy(): current_number = policy_model_cls.objects.all().count() assert current_number == 1 - assert policy.notify_level == NotifyLevel.notwatch + assert policy.notify_level == NotifyLevel.involved def test_notify_policy_existence(): project = f.ProjectFactory.create() assert not services.notify_policy_exists(project, project.owner) - services.create_notify_policy(project, project.owner, NotifyLevel.watch) + services.create_notify_policy(project, project.owner, NotifyLevel.all) assert services.notify_policy_exists(project, project.owner) @@ -145,23 +145,23 @@ def test_users_to_notify(): member1 = f.MembershipFactory.create(project=project, role=role1) policy_member1 = member1.user.notify_policies.get(project=project) - policy_member1.notify_level = NotifyLevel.ignore + policy_member1.notify_level = NotifyLevel.none policy_member1.save() member2 = f.MembershipFactory.create(project=project, role=role1) policy_member2 = member2.user.notify_policies.get(project=project) - policy_member2.notify_level = NotifyLevel.ignore + policy_member2.notify_level = NotifyLevel.none policy_member2.save() member3 = f.MembershipFactory.create(project=project, role=role1) policy_member3 = member3.user.notify_policies.get(project=project) - policy_member3.notify_level = NotifyLevel.ignore + policy_member3.notify_level = NotifyLevel.none policy_member3.save() member4 = f.MembershipFactory.create(project=project, role=role1) policy_member4 = member4.user.notify_policies.get(project=project) - policy_member4.notify_level = NotifyLevel.ignore + policy_member4.notify_level = NotifyLevel.none policy_member4.save() member5 = f.MembershipFactory.create(project=project, role=role2) policy_member5 = member5.user.notify_policies.get(project=project) - policy_member5.notify_level = NotifyLevel.ignore + policy_member5.notify_level = NotifyLevel.none policy_member5.save() inactive_member1 = f.MembershipFactory.create(project=project, role=role1) inactive_member1.user.is_active = False @@ -175,11 +175,11 @@ def test_users_to_notify(): policy_model_cls = apps.get_model("notifications", "NotifyPolicy") policy_inactive_member1 = policy_model_cls.objects.get(user=inactive_member1.user) - policy_inactive_member1.notify_level = NotifyLevel.watch + policy_inactive_member1.notify_level = NotifyLevel.all policy_inactive_member1.save() policy_system_member1 = policy_model_cls.objects.get(user=system_member1.user) - policy_system_member1.notify_level = NotifyLevel.watch + policy_system_member1.notify_level = NotifyLevel.all policy_system_member1.save() history = MagicMock() @@ -189,14 +189,14 @@ def test_users_to_notify(): # Test basic description modifications issue.description = "test1" issue.save() - policy_member4.notify_level = NotifyLevel.watch + policy_member4.notify_level = NotifyLevel.all policy_member4.save() users = services.get_users_to_notify(issue) assert len(users) == 1 assert tuple(users)[0] == issue.get_owner() # Test watch notify level in one member - policy_member1.notify_level = NotifyLevel.watch + policy_member1.notify_level = NotifyLevel.all policy_member1.save() users = services.get_users_to_notify(issue) @@ -205,14 +205,14 @@ def test_users_to_notify(): # Test with watchers issue.add_watcher(member3.user) - policy_member3.notify_level = NotifyLevel.watch + policy_member3.notify_level = NotifyLevel.all policy_member3.save() users = services.get_users_to_notify(issue) assert len(users) == 3 assert users == {member1.user, member3.user, issue.get_owner()} # Test with watchers with ignore policy - policy_member3.notify_level = NotifyLevel.ignore + policy_member3.notify_level = NotifyLevel.none policy_member3.save() issue.add_watcher(member3.user) @@ -251,7 +251,7 @@ def test_watching_users_to_notify_on_issue_modification_1(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.watch + watching_user_policy.notify_level = NotifyLevel.all users = services.get_users_to_notify(issue) assert users == {watching_user, issue.owner} @@ -260,7 +260,7 @@ def test_watching_users_to_notify_on_issue_modification_2(): # If: # - the user is watching the issue # - the user is not watching the project - # - the notify policy is notwatch + # - the notify policy is involved # Then: # - email is sent project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) @@ -270,7 +270,7 @@ def test_watching_users_to_notify_on_issue_modification_2(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.notwatch + watching_user_policy.notify_level = NotifyLevel.involved users = services.get_users_to_notify(issue) assert users == {watching_user, issue.owner} @@ -289,7 +289,7 @@ def test_watching_users_to_notify_on_issue_modification_3(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.ignore + watching_user_policy.notify_level = NotifyLevel.none watching_user_policy.save() users = services.get_users_to_notify(issue) assert users == {issue.owner} @@ -309,7 +309,7 @@ def test_watching_users_to_notify_on_issue_modification_4(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.ignore + watching_user_policy.notify_level = NotifyLevel.none watching_user_policy.save() users = services.get_users_to_notify(issue) assert users == {issue.owner} @@ -329,7 +329,7 @@ def test_watching_users_to_notify_on_issue_modification_5(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.watch + watching_user_policy.notify_level = NotifyLevel.all watching_user_policy.save() users = services.get_users_to_notify(issue) assert users == {watching_user, issue.owner} @@ -339,7 +339,7 @@ def test_watching_users_to_notify_on_issue_modification_6(): # If: # - the user is not watching the issue # - the user is watching the project - # - the notify policy is notwatch + # - the notify policy is involved # Then: # - email is sent project = f.ProjectFactory.create(anon_permissions= ["view_issues"]) @@ -349,7 +349,7 @@ def test_watching_users_to_notify_on_issue_modification_6(): watching_user_policy = services.get_notify_policy(project, watching_user) issue.description = "test1" issue.save() - watching_user_policy.notify_level = NotifyLevel.notwatch + watching_user_policy.notify_level = NotifyLevel.involved watching_user_policy.save() users = services.get_users_to_notify(issue) assert users == {watching_user, issue.owner} diff --git a/tests/integration/test_users.py b/tests/integration/test_users.py index d9990d45..3b435f0a 100644 --- a/tests/integration/test_users.py +++ b/tests/integration/test_users.py @@ -481,7 +481,7 @@ def test_get_watched_list_for_project_with_ignored_notify_level(): role = f.RoleFactory(project=project, permissions=["view_project", "view_us", "view_tasks", "view_issues"]) membership = f.MembershipFactory(project=project, role=role, user=fav_user) notify_policy = NotifyPolicy.objects.get(user=fav_user, project=project) - notify_policy.notify_level=NotifyLevel.ignore + notify_policy.notify_level=NotifyLevel.none notify_policy.save() watched_list = get_watched_list(fav_user, viewer_user) From 28c5104d230d3f3772ed156e2014a6dae37a6603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Barrag=C3=A1n=20Merino?= Date: Mon, 2 Nov 2015 09:26:28 +0100 Subject: [PATCH 190/190] [i18n] Update locales --- taiga/locale/ca/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/de/LC_MESSAGES/django.po | 16 ++++++++-------- taiga/locale/en/LC_MESSAGES/django.po | 2 +- taiga/locale/es/LC_MESSAGES/django.po | 12 ++++++------ taiga/locale/fi/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/fr/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/it/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/nl/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/pl/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/pt_BR/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/ru/LC_MESSAGES/django.po | 10 +++++----- taiga/locale/zh-Hant/LC_MESSAGES/django.po | 10 +++++----- 12 files changed, 60 insertions(+), 60 deletions(-) diff --git a/taiga/locale/ca/LC_MESSAGES/django.po b/taiga/locale/ca/LC_MESSAGES/django.po index 88083595..7ee77e1b 100644 --- a/taiga/locale/ca/LC_MESSAGES/django.po +++ b/taiga/locale/ca/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Catalan (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ca/)\n" @@ -1700,15 +1700,15 @@ msgid "roles" msgstr "rols" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/de/LC_MESSAGES/django.po b/taiga/locale/de/LC_MESSAGES/django.po index b754309b..8ce02f38 100644 --- a/taiga/locale/de/LC_MESSAGES/django.po +++ b/taiga/locale/de/LC_MESSAGES/django.po @@ -16,9 +16,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-31 01:42+0000\n" -"Last-Translator: Silsha Fux \n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: German (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/de/)\n" "MIME-Version: 1.0\n" @@ -1875,15 +1875,15 @@ msgid "roles" msgstr "Rollen" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" -msgstr "Nicht beobachtend" +msgid "Involved" +msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" -msgstr "Beobachtend" +msgid "All" +msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/en/LC_MESSAGES/django.po b/taiga/locale/en/LC_MESSAGES/django.po index 13f273ce..f9ac0a78 100644 --- a/taiga/locale/en/LC_MESSAGES/django.po +++ b/taiga/locale/en/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Taiga Dev Team \n" diff --git a/taiga/locale/es/LC_MESSAGES/django.po b/taiga/locale/es/LC_MESSAGES/django.po index af675f2f..25b57f95 100644 --- a/taiga/locale/es/LC_MESSAGES/django.po +++ b/taiga/locale/es/LC_MESSAGES/django.po @@ -12,9 +12,9 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:28+0000\n" -"Last-Translator: David Barragán \n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" +"Last-Translator: Taiga Dev Team \n" "Language-Team: Spanish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/es/)\n" "MIME-Version: 1.0\n" @@ -1868,15 +1868,15 @@ msgid "roles" msgstr "roles" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/fi/LC_MESSAGES/django.po b/taiga/locale/fi/LC_MESSAGES/django.po index 1a8e4f83..139549cb 100644 --- a/taiga/locale/fi/LC_MESSAGES/django.po +++ b/taiga/locale/fi/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Finnish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fi/)\n" @@ -1829,15 +1829,15 @@ msgid "roles" msgstr "roolit" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/fr/LC_MESSAGES/django.po b/taiga/locale/fr/LC_MESSAGES/django.po index 65e6a717..b4601396 100644 --- a/taiga/locale/fr/LC_MESSAGES/django.po +++ b/taiga/locale/fr/LC_MESSAGES/django.po @@ -17,8 +17,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: French (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/fr/)\n" @@ -1801,15 +1801,15 @@ msgid "roles" msgstr "rôles" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/it/LC_MESSAGES/django.po b/taiga/locale/it/LC_MESSAGES/django.po index 4dda1293..80f6b8ed 100644 --- a/taiga/locale/it/LC_MESSAGES/django.po +++ b/taiga/locale/it/LC_MESSAGES/django.po @@ -13,8 +13,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Italian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/it/)\n" @@ -1963,15 +1963,15 @@ msgid "roles" msgstr "ruoli" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/nl/LC_MESSAGES/django.po b/taiga/locale/nl/LC_MESSAGES/django.po index 0d4eb446..fd7c7502 100644 --- a/taiga/locale/nl/LC_MESSAGES/django.po +++ b/taiga/locale/nl/LC_MESSAGES/django.po @@ -9,8 +9,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Dutch (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/nl/)\n" @@ -1765,15 +1765,15 @@ msgid "roles" msgstr "rollen" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/pl/LC_MESSAGES/django.po b/taiga/locale/pl/LC_MESSAGES/django.po index 08806b3f..ba47a433 100644 --- a/taiga/locale/pl/LC_MESSAGES/django.po +++ b/taiga/locale/pl/LC_MESSAGES/django.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Polish (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/pl/)\n" @@ -1871,15 +1871,15 @@ msgid "roles" msgstr "role" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/pt_BR/LC_MESSAGES/django.po b/taiga/locale/pt_BR/LC_MESSAGES/django.po index d1897d3a..244f5689 100644 --- a/taiga/locale/pt_BR/LC_MESSAGES/django.po +++ b/taiga/locale/pt_BR/LC_MESSAGES/django.po @@ -18,8 +18,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Portuguese (Brazil) (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/pt_BR/)\n" @@ -1876,15 +1876,15 @@ msgid "roles" msgstr "funções" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/ru/LC_MESSAGES/django.po b/taiga/locale/ru/LC_MESSAGES/django.po index 8c4e2604..8d75d1e5 100644 --- a/taiga/locale/ru/LC_MESSAGES/django.po +++ b/taiga/locale/ru/LC_MESSAGES/django.po @@ -12,8 +12,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Russian (http://www.transifex.com/taiga-agile-llc/taiga-back/" "language/ru/)\n" @@ -1882,15 +1882,15 @@ msgid "roles" msgstr "роли" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61 diff --git a/taiga/locale/zh-Hant/LC_MESSAGES/django.po b/taiga/locale/zh-Hant/LC_MESSAGES/django.po index a08a2337..240baf91 100644 --- a/taiga/locale/zh-Hant/LC_MESSAGES/django.po +++ b/taiga/locale/zh-Hant/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgid "" msgstr "" "Project-Id-Version: taiga-back\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-11-02 09:09+0100\n" -"PO-Revision-Date: 2015-10-30 17:20+0000\n" +"POT-Creation-Date: 2015-11-02 09:24+0100\n" +"PO-Revision-Date: 2015-11-02 08:25+0000\n" "Last-Translator: Taiga Dev Team \n" "Language-Team: Chinese Traditional (http://www.transifex.com/taiga-agile-llc/" "taiga-back/language/zh-Hant/)\n" @@ -1851,15 +1851,15 @@ msgid "roles" msgstr "角色" #: taiga/projects/notifications/choices.py:28 -msgid "Not watching" +msgid "Involved" msgstr "" #: taiga/projects/notifications/choices.py:29 -msgid "Watching" +msgid "All" msgstr "" #: taiga/projects/notifications/choices.py:30 -msgid "Ignoring" +msgid "None" msgstr "" #: taiga/projects/notifications/models.py:61