Add gettext to translatable strings

remotes/origin/enhancement/email-actions
David Barragán Merino 2015-03-27 13:31:36 +01:00
parent 2d960c7a5d
commit d3fade9565
48 changed files with 524 additions and 211 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: taiga-back\n" "Project-Id-Version: taiga-back\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-03-25 20:09+0100\n" "POT-Creation-Date: 2015-04-06 17:04+0200\n"
"PO-Revision-Date: 2015-03-25 20:09+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n"
"Last-Translator: Taiga Dev Team <support@taiga.io>\n" "Last-Translator: Taiga Dev Team <support@taiga.io>\n"
"Language-Team: Taiga Dev Team <support@taiga.io>\n" "Language-Team: Taiga Dev Team <support@taiga.io>\n"

View File

@ -17,7 +17,7 @@
from functools import partial from functools import partial
from enum import Enum from enum import Enum
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from rest_framework import serializers from rest_framework import serializers

View File

@ -18,6 +18,7 @@ from rest_framework import serializers
from django.core import validators from django.core import validators
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _
import re import re
@ -29,13 +30,13 @@ class BaseRegisterSerializer(serializers.Serializer):
def validate_username(self, attrs, source): def validate_username(self, attrs, source):
value = attrs[source] value = attrs[source]
validator = validators.RegexValidator(re.compile('^[\w.-]+$'), "invalid username", "invalid") validator = validators.RegexValidator(re.compile('^[\w.-]+$'), _("invalid username"), "invalid")
try: try:
validator(value) validator(value)
except ValidationError: except ValidationError:
raise serializers.ValidationError("Required. 255 characters or fewer. Letters, numbers " raise serializers.ValidationError(_("Required. 255 characters or fewer. Letters, numbers "
"and /./-/_ characters'") "and /./-/_ characters'"))
return attrs return attrs

View File

@ -91,7 +91,7 @@ def get_membership_by_token(token:str):
membership_model = apps.get_model("projects", "Membership") membership_model = apps.get_model("projects", "Membership")
qs = membership_model.objects.filter(token=token) qs = membership_model.objects.filter(token=token)
if len(qs) == 0: if len(qs) == 0:
raise exc.NotFound("Token not matches any valid invitation.") raise exc.NotFound(_("Token not matches any valid invitation."))
return qs[0] return qs[0]
@ -119,7 +119,7 @@ def public_register(username:str, password:str, email:str, full_name:str):
try: try:
user.save() user.save()
except IntegrityError: except IntegrityError:
raise exc.WrongArguments("User is already register.") raise exc.WrongArguments(_("User is already register."))
send_register_email(user) send_register_email(user)
user_registered_signal.send(sender=user.__class__, user=user) user_registered_signal.send(sender=user.__class__, user=user)
@ -143,7 +143,7 @@ def private_register_for_existing_user(token:str, username:str, password:str):
membership.user = user membership.user = user
membership.save(update_fields=["user"]) membership.save(update_fields=["user"])
except IntegrityError: except IntegrityError:
raise exc.IntegrityError("Membership with user is already exists.") raise exc.IntegrityError(_("Membership with user is already exists."))
send_register_email(user) send_register_email(user)
return user return user

View File

@ -18,6 +18,7 @@ from taiga.base import exceptions as exc
from django.apps import apps from django.apps import apps
from django.core import signing from django.core import signing
from django.utils.translation import ugettext as _
def get_token_for_user(user, scope): def get_token_for_user(user, scope):
@ -43,13 +44,13 @@ def get_user_for_token(token, scope, max_age=None):
try: try:
data = signing.loads(token, max_age=max_age) data = signing.loads(token, max_age=max_age)
except signing.BadSignature: except signing.BadSignature:
raise exc.NotAuthenticated("Invalid token") raise exc.NotAuthenticated(_("Invalid token"))
model_cls = apps.get_model("users", "User") model_cls = apps.get_model("users", "User")
try: try:
user = model_cls.objects.get(pk=data["user_%s_id" % (scope)]) user = model_cls.objects.get(pk=data["user_%s_id" % (scope)])
except (model_cls.DoesNotExist, KeyError): except (model_cls.DoesNotExist, KeyError):
raise exc.NotAuthenticated("Invalid token") raise exc.NotAuthenticated(_("Invalid token"))
else: else:
return user return user

View File

@ -126,11 +126,11 @@ class GenericAPIView(views.APIView):
""" """
deprecated_style = False deprecated_style = False
if page_size is not None: if page_size is not None:
warnings.warn('The `page_size` parameter to `paginate_queryset()` ' warnings.warn(_('The `page_size` parameter to `paginate_queryset()` '
'is due to be deprecated. ' 'is due to be deprecated. '
'Note that the return style of this method is also ' 'Note that the return style of this method is also '
'changed, and will simply return a page object ' 'changed, and will simply return a page object '
'when called without a `page_size` argument.', 'when called without a `page_size` argument.'),
PendingDeprecationWarning, stacklevel=2) PendingDeprecationWarning, stacklevel=2)
deprecated_style = True deprecated_style = True
else: else:
@ -141,12 +141,10 @@ class GenericAPIView(views.APIView):
return None return None
if not self.allow_empty: if not self.allow_empty:
warnings.warn( warnings.warn(_('The `allow_empty` parameter is due to be deprecated. '
'The `allow_empty` parameter is due to be deprecated. ' 'To use `allow_empty=False` style behavior, You should override '
'To use `allow_empty=False` style behavior, You should override ' '`get_queryset()` and explicitly raise a 404 on empty querysets.'),
'`get_queryset()` and explicitly raise a 404 on empty querysets.', PendingDeprecationWarning, stacklevel=2)
PendingDeprecationWarning, stacklevel=2
)
paginator = self.paginator_class(queryset, page_size, paginator = self.paginator_class(queryset, page_size,
allow_empty_first_page=self.allow_empty) allow_empty_first_page=self.allow_empty)
@ -191,10 +189,10 @@ class GenericAPIView(views.APIView):
""" """
filter_backends = self.filter_backends or [] filter_backends = self.filter_backends or []
if not filter_backends and hasattr(self, 'filter_backend'): if not filter_backends and hasattr(self, 'filter_backend'):
raise RuntimeError('The `filter_backend` attribute and `FILTER_BACKEND` setting ' raise RuntimeError(_('The `filter_backend` attribute and `FILTER_BACKEND` setting '
'are due to be deprecated in favor of a `filter_backends` ' 'are due to be deprecated in favor of a `filter_backends` '
'attribute and `DEFAULT_FILTER_BACKENDS` setting, that take ' 'attribute and `DEFAULT_FILTER_BACKENDS` setting, that take '
'a *list* of filter backend classes.') 'a *list* of filter backend classes.'))
return filter_backends return filter_backends
########################################################### ###########################################################
@ -212,8 +210,8 @@ class GenericAPIView(views.APIView):
Otherwise defaults to using `self.paginate_by`. Otherwise defaults to using `self.paginate_by`.
""" """
if queryset is not None: if queryset is not None:
raise RuntimeError('The `queryset` parameter to `get_paginate_by()` ' raise RuntimeError(_('The `queryset` parameter to `get_paginate_by()` '
'is due to be deprecated.') 'is due to be deprecated.'))
if self.paginate_by_param: if self.paginate_by_param:
try: try:
return strict_positive_int( return strict_positive_int(
@ -233,11 +231,9 @@ class GenericAPIView(views.APIView):
if serializer_class is not None: if serializer_class is not None:
return serializer_class return serializer_class
assert self.model is not None, \ assert self.model is not None, _("'%s' should either include a 'serializer_class' attribute, "
"'%s' should either include a 'serializer_class' attribute, " \ "or use the 'model' attribute as a shortcut for "
"or use the 'model' attribute as a shortcut for " \ "automatically generating a serializer class." % self.__class__.__name__)
"automatically generating a serializer class." \
% self.__class__.__name__
class DefaultSerializer(self.model_serializer_class): class DefaultSerializer(self.model_serializer_class):
class Meta: class Meta:
@ -261,7 +257,7 @@ class GenericAPIView(views.APIView):
if self.model is not None: if self.model is not None:
return self.model._default_manager.all() return self.model._default_manager.all()
raise ImproperlyConfigured("'%s' must define 'queryset' or 'model'" % self.__class__.__name__) raise ImproperlyConfigured(_("'%s' must define 'queryset' or 'model'" % self.__class__.__name__))
def get_object(self, queryset=None): def get_object(self, queryset=None):
""" """
@ -289,18 +285,16 @@ class GenericAPIView(views.APIView):
if lookup is not None: if lookup is not None:
filter_kwargs = {self.lookup_field: lookup} filter_kwargs = {self.lookup_field: lookup}
elif pk is not None and self.lookup_field == 'pk': elif pk is not None and self.lookup_field == 'pk':
raise RuntimeError('The `pk_url_kwarg` attribute is due to be deprecated. ' raise RuntimeError(_('The `pk_url_kwarg` attribute is due to be deprecated. '
'Use the `lookup_field` attribute instead') 'Use the `lookup_field` attribute instead'))
elif slug is not None and self.lookup_field == 'pk': elif slug is not None and self.lookup_field == 'pk':
raise RuntimeError('The `slug_url_kwarg` attribute is due to be deprecated. ' raise RuntimeError(_('The `slug_url_kwarg` attribute is due to be deprecated. '
'Use the `lookup_field` attribute instead') 'Use the `lookup_field` attribute instead'))
else: else:
raise ImproperlyConfigured( raise ImproperlyConfigured(_('Expected view %s to be called with a URL keyword argument '
'Expected view %s to be called with a URL keyword argument ' 'named "%s". Fix your URL conf, or set the `.lookup_field` '
'named "%s". Fix your URL conf, or set the `.lookup_field` ' 'attribute on the view correctly.' %
'attribute on the view correctly.' % (self.__class__.__name__, self.lookup_field)))
(self.__class__.__name__, self.lookup_field)
)
obj = get_object_or_404(queryset, **filter_kwargs) obj = get_object_or_404(queryset, **filter_kwargs)
return obj return obj

View File

@ -22,6 +22,7 @@ import warnings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.http import Http404 from django.http import Http404
from django.db import transaction as tx from django.db import transaction as tx
from django.utils.translation import ugettext as _
from taiga.base import response from taiga.base import response
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -94,12 +95,10 @@ class ListModelMixin(object):
# Default is to allow empty querysets. This can be altered by setting # Default is to allow empty querysets. This can be altered by setting
# `.allow_empty = False`, to raise 404 errors on empty querysets. # `.allow_empty = False`, to raise 404 errors on empty querysets.
if not self.allow_empty and not self.object_list: if not self.allow_empty and not self.object_list:
warnings.warn( warnings.warn(_('The `allow_empty` parameter is due to be deprecated. '
'The `allow_empty` parameter is due to be deprecated. ' 'To use `allow_empty=False` style behavior, You should override '
'To use `allow_empty=False` style behavior, You should override ' '`get_queryset()` and explicitly raise a 404 on empty querysets.'),
'`get_queryset()` and explicitly raise a 404 on empty querysets.', PendingDeprecationWarning)
PendingDeprecationWarning
)
class_name = self.__class__.__name__ class_name = self.__class__.__name__
error_msg = self.empty_error % {'class_name': class_name} error_msg = self.empty_error % {'class_name': class_name}
raise Http404(error_msg) raise Http404(error_msg)

View File

@ -20,6 +20,7 @@ from taiga.base.utils import sequence as sq
from taiga.permissions.service import user_has_perm, is_project_owner from taiga.permissions.service import user_has_perm, is_project_owner
from django.apps import apps from django.apps import apps
from django.utils.translation import ugettext as _
###################################################################### ######################################################################
# Base permissiones definition # Base permissiones definition
@ -57,7 +58,7 @@ class ResourcePermission(object):
elif inspect.isclass(permset) and issubclass(permset, PermissionComponent): elif inspect.isclass(permset) and issubclass(permset, PermissionComponent):
permset = permset() permset = permset()
else: else:
raise RuntimeError("Invalid permission definition.") raise RuntimeError(_("Invalid permission definition."))
if self.global_perms: if self.global_perms:
permset = (self.global_perms & permset) permset = (self.global_perms & permset)

View File

@ -23,6 +23,7 @@ from django.core.exceptions import PermissionDenied
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.utils.translation import ugettext as _
from rest_framework import status, exceptions from rest_framework import status, exceptions
from rest_framework.compat import smart_text, HttpResponseBase, View from rest_framework.compat import smart_text, HttpResponseBase, View
@ -93,10 +94,10 @@ def exception_handler(exc):
headers=headers) headers=headers)
elif isinstance(exc, Http404): elif isinstance(exc, Http404):
return NotFound({'detail': 'Not found'}) return NotFound({'detail': _('Not found')})
elif isinstance(exc, PermissionDenied): elif isinstance(exc, PermissionDenied):
return Forbidden({'detail': 'Permission denied'}) return Forbidden({'detail': _('Permission denied')})
# Note: Unhandled exceptions will raise a 500 error. # Note: Unhandled exceptions will raise a 500 error.
return None return None
@ -345,11 +346,9 @@ class APIView(View):
Returns the final response object. Returns the final response object.
""" """
# Make the error obvious if a proper response is not returned # Make the error obvious if a proper response is not returned
assert isinstance(response, HttpResponseBase), ( assert isinstance(response, HttpResponseBase), _('Expected a `Response`, `HttpResponse` or '
'Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` ' '`HttpStreamingResponse` to be returned from the view, '
'to be returned from the view, but received a `%s`' 'but received a `%s`' % type(response))
% type(response)
)
if isinstance(response, Response): if isinstance(response, Response):
if not getattr(request, 'accepted_renderer', None): if not getattr(request, 'accepted_renderer', None):
@ -446,6 +445,6 @@ class APIView(View):
def api_server_error(request, *args, **kwargs): 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['CONTENT_TYPE'] == "application/json":
return HttpResponse(json.dumps({"error": "Server application error"}), return HttpResponse(json.dumps({"error": _("Server application error")}),
status=status.HTTP_500_INTERNAL_SERVER_ERROR) status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return server_error(request, *args, **kwargs) return server_error(request, *args, **kwargs)

View File

@ -19,6 +19,7 @@
from functools import update_wrapper from functools import update_wrapper
from django.utils.decorators import classonlymethod from django.utils.decorators import classonlymethod
from django.utils.translation import ugettext as _
from . import views from . import views
from . import mixins from . import mixins
@ -53,12 +54,12 @@ class ViewSetMixin(object):
# sanitize keyword arguments # sanitize keyword arguments
for key in initkwargs: for key in initkwargs:
if key in cls.http_method_names: if key in cls.http_method_names:
raise TypeError("You tried to pass in the %s method name as a " raise TypeError(_("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that." "keyword argument to %s(). Don't do that."
% (key, cls.__name__)) % (key, cls.__name__)))
if not hasattr(cls, key): if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r" % ( raise TypeError(_("%s() received an invalid keyword %r"
cls.__name__, key)) % (cls.__name__, key)))
def view(request, *args, **kwargs): def view(request, *args, **kwargs):
self = cls(**initkwargs) self = cls(**initkwargs)

View File

@ -19,7 +19,7 @@ import logging
from django.apps import apps from django.apps import apps
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import filters from rest_framework import filters
@ -60,7 +60,7 @@ class QueryParamsFilterMixin(filters.BaseFilterBackend):
try: try:
queryset = queryset.filter(**query_params) queryset = queryset.filter(**query_params)
except ValueError: except ValueError:
raise exc.BadRequest("Error in filter params types.") raise exc.BadRequest(_("Error in filter params types."))
return queryset return queryset
@ -104,10 +104,10 @@ class PermissionBasedFilterBackend(FilterBackend):
try: try:
project_id = int(request.QUERY_PARAMS["project"]) project_id = int(request.QUERY_PARAMS["project"])
except: except:
logger.error("Filtering project diferent value than an integer: {}".format( logger.error(_("Filtering project diferent value than an integer: {}".format(
request.QUERY_PARAMS["project"] request.QUERY_PARAMS["project"]
)) )))
raise exc.BadRequest("'project' must be an integer value.") raise exc.BadRequest(_("'project' must be an integer value."))
qs = queryset qs = queryset
@ -193,10 +193,10 @@ class CanViewProjectObjFilterBackend(FilterBackend):
try: try:
project_id = int(request.QUERY_PARAMS["project"]) project_id = int(request.QUERY_PARAMS["project"])
except: except:
logger.error("Filtering project diferent value than an integer: {}".format( logger.error(_("Filtering project diferent value than an integer: {}".format(
request.QUERY_PARAMS["project"] request.QUERY_PARAMS["project"]
)) )))
raise exc.BadRequest("'project' must be an integer value.") raise exc.BadRequest(_("'project' must be an integer value."))
qs = queryset qs = queryset
@ -250,8 +250,9 @@ class MembersFilterBackend(PermissionBasedFilterBackend):
try: try:
project_id = int(request.QUERY_PARAMS["project"]) project_id = int(request.QUERY_PARAMS["project"])
except: except:
logger.error("Filtering project diferent value than an integer: {}".format(request.QUERY_PARAMS["project"])) logger.error(_("Filtering project diferent value than an integer: {}".format(
raise exc.BadRequest("'project' must be an integer value.") request.QUERY_PARAMS["project"])))
raise exc.BadRequest(_("'project' must be an integer value."))
if project_id: if project_id:
Project = apps.get_model('projects', 'Project') Project = apps.get_model('projects', 'Project')

View File

@ -15,6 +15,8 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext as _
from contextlib import contextmanager from contextlib import contextmanager
@ -22,7 +24,7 @@ from contextlib import contextmanager
def without_signals(*disablers): def without_signals(*disablers):
for disabler in disablers: for disabler in disablers:
if not (isinstance(disabler, list) or isinstance(disabler, tuple)) or len(disabler) == 0: if not (isinstance(disabler, list) or isinstance(disabler, tuple)) or len(disabler) == 0:
raise ValueError("The parameters must be lists of at least one parameter (the signal)") raise ValueError(_("The parameters must be lists of at least one parameter (the signal)."))
signal, *ids = disabler signal, *ids = disabler
signal.backup_receivers = signal.receivers signal.backup_receivers = signal.receivers

View File

@ -19,7 +19,7 @@ import codecs
import uuid import uuid
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.db.transaction import atomic from django.db.transaction import atomic
from django.db.models import signals from django.db.models import signals
from django.conf import settings from django.conf import settings

View File

@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext as _
from taiga.projects.models import Membership from taiga.projects.models import Membership
from . import serializers from . import serializers
@ -83,7 +85,7 @@ def dict_to_project(data, owner=None):
project_serialized = service.store_project(data) project_serialized = service.store_project(data)
if not project_serialized: if not project_serialized:
raise TaigaImportError('error importing project') raise TaigaImportError(_('error importing project'))
proj = project_serialized.object proj = project_serialized.object
@ -96,12 +98,12 @@ def dict_to_project(data, owner=None):
service.store_choices(proj, data, "severities", serializers.SeverityExportSerializer) service.store_choices(proj, data, "severities", serializers.SeverityExportSerializer)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing choices') raise TaigaImportError(_('error importing choices'))
service.store_default_choices(proj, data) service.store_default_choices(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing default choices') raise TaigaImportError(_('error importing default choices'))
service.store_custom_attributes(proj, data, "userstorycustomattributes", service.store_custom_attributes(proj, data, "userstorycustomattributes",
serializers.UserStoryCustomAttributeExportSerializer) serializers.UserStoryCustomAttributeExportSerializer)
@ -111,12 +113,12 @@ def dict_to_project(data, owner=None):
serializers.IssueCustomAttributeExportSerializer) serializers.IssueCustomAttributeExportSerializer)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing custom attributes') raise TaigaImportError(_('error importing custom attributes'))
service.store_roles(proj, data) service.store_roles(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing roles') raise TaigaImportError(_('error importing roles'))
service.store_memberships(proj, data) service.store_memberships(proj, data)
@ -131,37 +133,37 @@ def dict_to_project(data, owner=None):
) )
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing memberships') raise TaigaImportError(_('error importing memberships'))
store_milestones(proj, data) store_milestones(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing milestones') raise TaigaImportError(_('error importing milestones'))
store_wiki_pages(proj, data) store_wiki_pages(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing wiki pages') raise TaigaImportError(_('error importing wiki pages'))
store_wiki_links(proj, data) store_wiki_links(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing wiki links') raise TaigaImportError(_('error importing wiki links'))
store_issues(proj, data) store_issues(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing issues') raise TaigaImportError(_('error importing issues'))
store_user_stories(proj, data) store_user_stories(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing user stories') raise TaigaImportError(_('error importing user stories'))
store_tasks(proj, data) store_tasks(proj, data)
if service.get_errors(clear=False): if service.get_errors(clear=False):
raise TaigaImportError('error importing issues') raise TaigaImportError(_('error importing issues'))
store_tags_colors(proj, data) store_tags_colors(proj, data)

View File

@ -18,11 +18,12 @@ import base64
import os import os
from collections import OrderedDict from collections import OrderedDict
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext as _
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers from rest_framework import serializers
@ -153,7 +154,7 @@ class ProjectRelatedField(serializers.RelatedField):
kwargs = {self.slug_field: data, "project": self.context['project']} kwargs = {self.slug_field: data, "project": self.context['project']}
return self.queryset.get(**kwargs) return self.queryset.get(**kwargs)
except ObjectDoesNotExist: except ObjectDoesNotExist:
raise ValidationError("{}=\"{}\" not found in this project".format(self.slug_field, data)) raise ValidationError(_("{}=\"{}\" not found in this project".format(self.slug_field, data)))
class HistoryUserField(JsonField): class HistoryUserField(JsonField):
@ -458,7 +459,7 @@ class MilestoneExportSerializer(serializers.ModelSerializer):
name = attrs[source] name = attrs[source]
qs = self.project.milestones.filter(name=name) qs = self.project.milestones.filter(name=name)
if qs.exists(): if qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs

View File

@ -20,6 +20,7 @@ from django.core.files.storage import default_storage
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.utils import timezone from django.utils import timezone
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _
from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail
@ -45,8 +46,8 @@ def dump_project(self, user, project):
except Exception: except Exception:
ctx = { ctx = {
"user": user, "user": user,
"error_subject": "Error generating project dump", "error_subject": _("Error generating project dump"),
"error_message": "Error generating project dump", "error_message": _("Error generating project dump"),
"project": project "project": project
} }
email = mbuilder.export_error(user, ctx) email = mbuilder.export_error(user, ctx)
@ -78,8 +79,8 @@ def load_project_dump(user, dump):
except Exception: except Exception:
ctx = { ctx = {
"user": user, "user": user,
"error_subject": "Error loading project dump", "error_subject": _("Error loading project dump"),
"error_message": "Error loading project dump", "error_message": _("Error loading project dump"),
} }
email = mbuilder.import_error(user, ctx) email = mbuilder.import_error(user, ctx)
email.send() email.send()

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base import response from taiga.base import response

View File

@ -16,7 +16,7 @@
import re import re
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
@ -27,11 +27,10 @@ from taiga.projects.history.services import take_snapshot
from taiga.projects.notifications.services import send_notifications from taiga.projects.notifications.services import send_notifications
from taiga.hooks.event_hooks import BaseEventHook from taiga.hooks.event_hooks import BaseEventHook
from taiga.hooks.exceptions import ActionSyntaxException from taiga.hooks.exceptions import ActionSyntaxException
from taiga.base.utils import json
from .services import get_bitbucket_user from .services import get_bitbucket_user
import json
class PushEventHook(BaseEventHook): class PushEventHook(BaseEventHook):
def process_event(self): def process_event(self):
@ -92,7 +91,7 @@ class PushEventHook(BaseEventHook):
element.save() element.save()
snapshot = take_snapshot(element, snapshot = take_snapshot(element,
comment="Status changed from BitBucket commit", comment=_("Status changed from BitBucket commit"),
user=get_bitbucket_user(bitbucket_user)) user=get_bitbucket_user(bitbucket_user))
send_notifications(element, history=snapshot) send_notifications(element, history=snapshot)

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
@ -85,7 +85,7 @@ class PushEventHook(BaseEventHook):
element.save() element.save()
snapshot = take_snapshot(element, snapshot = take_snapshot(element,
comment="Status changed from GitHub commit", comment=_("Status changed from GitHub commit"),
user=get_github_user(github_user)) user=get_github_user(github_user))
send_notifications(element, history=snapshot) send_notifications(element, history=snapshot)
@ -93,7 +93,7 @@ class PushEventHook(BaseEventHook):
def replace_github_references(project_url, wiki_text): def replace_github_references(project_url, wiki_text):
if wiki_text == None: if wiki_text == None:
wiki_text = "" wiki_text = ""
template = "\g<1>[GitHub#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url) template = "\g<1>[GitHub#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M) return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
@ -125,7 +125,7 @@ class IssuesEventHook(BaseEventHook):
) )
take_snapshot(issue, user=get_github_user(github_user)) take_snapshot(issue, user=get_github_user(github_user))
snapshot = take_snapshot(issue, comment="Created from GitHub", user=get_github_user(github_user)) snapshot = take_snapshot(issue, comment=_("Created from GitHub"), user=get_github_user(github_user))
send_notifications(issue, history=snapshot) send_notifications(issue, history=snapshot)
@ -149,6 +149,6 @@ class IssueCommentEventHook(BaseEventHook):
for item in list(issues) + list(tasks) + list(uss): for item in list(issues) + list(tasks) + list(uss):
snapshot = take_snapshot(item, snapshot = take_snapshot(item,
comment="From GitHub:\n\n{}".format(comment_message), comment=_("From GitHub:\n\n{}".format(comment_message)),
user=get_github_user(github_user)) user=get_github_user(github_user))
send_notifications(item, history=snapshot) send_notifications(item, history=snapshot)

View File

@ -17,7 +17,7 @@
import re import re
import os import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
@ -84,7 +84,7 @@ class PushEventHook(BaseEventHook):
element.save() element.save()
snapshot = take_snapshot(element, snapshot = take_snapshot(element,
comment="Status changed from GitLab commit", comment=_("Status changed from GitLab commit"),
user=get_gitlab_user(gitlab_user)) user=get_gitlab_user(gitlab_user))
send_notifications(element, history=snapshot) send_notifications(element, history=snapshot)
@ -126,5 +126,5 @@ class IssuesEventHook(BaseEventHook):
) )
take_snapshot(issue, user=get_gitlab_user(None)) take_snapshot(issue, user=get_gitlab_user(None))
snapshot = take_snapshot(issue, comment="Created from GitLab", user=get_gitlab_user(None)) snapshot = take_snapshot(issue, comment=_("Created from GitLab"), user=get_gitlab_user(None))
send_notifications(issue, history=snapshot) send_notifications(issue, history=snapshot)

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: taiga-back\n" "Project-Id-Version: taiga-back\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-03-25 20:09+0100\n" "POT-Creation-Date: 2015-04-06 17:04+0200\n"
"PO-Revision-Date: 2015-03-25 20:09+0100\n" "PO-Revision-Date: 2015-03-25 20:09+0100\n"
"Last-Translator: Taiga Dev Team <support@taiga.io>\n" "Last-Translator: Taiga Dev Team <support@taiga.io>\n"
"Language-Team: Taiga Dev Team <support@taiga.io>\n" "Language-Team: Taiga Dev Team <support@taiga.io>\n"
@ -28,6 +28,15 @@ msgstr ""
msgid "invalid login type" msgid "invalid login type"
msgstr "" msgstr ""
#: taiga/auth/serializers.py:33 taiga/users/serializers.py:52
msgid "invalid username"
msgstr ""
#: taiga/auth/serializers.py:38 taiga/users/serializers.py:58
msgid ""
"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'"
msgstr ""
#: taiga/auth/services.py:75 #: taiga/auth/services.py:75
msgid "Username is already in use." msgid "Username is already in use."
msgstr "" msgstr ""
@ -36,19 +45,129 @@ msgstr ""
msgid "Email is already in use." msgid "Email is already in use."
msgstr "" msgstr ""
#: taiga/auth/services.py:94
msgid "Token not matches any valid invitation."
msgstr ""
#: taiga/auth/services.py:122
msgid "User is already register."
msgstr ""
#: taiga/auth/services.py:146
msgid "Membership with user is already exists."
msgstr ""
#: taiga/auth/services.py:172 #: taiga/auth/services.py:172
msgid "Error on creating new user." msgid "Error on creating new user."
msgstr "" msgstr ""
#: taiga/base/api/generics.py:162 #: taiga/auth/tokens.py:47 taiga/auth/tokens.py:54
msgid "Invalid token"
msgstr ""
#: taiga/base/api/generics.py:129
msgid ""
"The `page_size` parameter to `paginate_queryset()` is due to be deprecated. "
"Note that the return style of this method is also changed, and will simply "
"return a page object when called without a `page_size` argument."
msgstr ""
#: taiga/base/api/generics.py:144 taiga/base/api/mixins.py:98
msgid ""
"The `allow_empty` parameter is due to be deprecated. To use "
"`allow_empty=False` style behavior, You should override `get_queryset()` and "
"explicitly raise a 404 on empty querysets."
msgstr ""
#: taiga/base/api/generics.py:160
msgid "Page is not 'last', nor can it be converted to an int." msgid "Page is not 'last', nor can it be converted to an int."
msgstr "" msgstr ""
#: taiga/base/api/generics.py:166 #: taiga/base/api/generics.py:164
#, python-format #, python-format
msgid "Invalid page (%(page_number)s): %(message)s" msgid "Invalid page (%(page_number)s): %(message)s"
msgstr "" msgstr ""
#: taiga/base/api/generics.py:192
msgid ""
"The `filter_backend` attribute and `FILTER_BACKEND` setting are due to be "
"deprecated in favor of a `filter_backends` attribute and "
"`DEFAULT_FILTER_BACKENDS` setting, that take a *list* of filter backend "
"classes."
msgstr ""
#: taiga/base/api/generics.py:213
msgid ""
"The `queryset` parameter to `get_paginate_by()` is due to be deprecated."
msgstr ""
#: taiga/base/api/generics.py:234
#, python-format
msgid ""
"'%s' should either include a 'serializer_class' attribute, or use the "
"'model' attribute as a shortcut for automatically generating a serializer "
"class."
msgstr ""
#: taiga/base/api/generics.py:260
#, python-format
msgid "'%s' must define 'queryset' or 'model'"
msgstr ""
#: taiga/base/api/generics.py:288
msgid ""
"The `pk_url_kwarg` attribute is due to be deprecated. Use the `lookup_field` "
"attribute instead"
msgstr ""
#: taiga/base/api/generics.py:291
msgid ""
"The `slug_url_kwarg` attribute is due to be deprecated. Use the "
"`lookup_field` attribute instead"
msgstr ""
#: taiga/base/api/generics.py:294
#, python-format
msgid ""
"Expected view %s to be called with a URL keyword argument named \"%s\". Fix "
"your URL conf, or set the `.lookup_field` attribute on the view correctly."
msgstr ""
#: taiga/base/api/permissions.py:61
msgid "Invalid permission definition."
msgstr ""
#: taiga/base/api/views.py:97
msgid "Not found"
msgstr ""
#: taiga/base/api/views.py:100
msgid "Permission denied"
msgstr ""
#: taiga/base/api/views.py:349
#, python-format
msgid ""
"Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be "
"returned from the view, but received a `%s`"
msgstr ""
#: taiga/base/api/views.py:448
msgid "Server application error"
msgstr ""
#: taiga/base/api/viewsets.py:57
#, python-format
msgid ""
"You tried to pass in the %s method name as a keyword argument to %s(). Don't "
"do that."
msgstr ""
#: taiga/base/api/viewsets.py:61
#, python-format
msgid "%s() received an invalid keyword %r"
msgstr ""
#: taiga/base/connectors/exceptions.py:24 #: taiga/base/connectors/exceptions.py:24
msgid "Connection error." msgid "Connection error."
msgstr "" msgstr ""
@ -81,6 +200,20 @@ msgstr ""
msgid "Precondition error" msgid "Precondition error"
msgstr "" msgstr ""
#: taiga/base/filters.py:63
msgid "Error in filter params types."
msgstr ""
#: taiga/base/filters.py:107 taiga/base/filters.py:196
#: taiga/base/filters.py:253
msgid "Filtering project diferent value than an integer: {}"
msgstr ""
#: taiga/base/filters.py:110 taiga/base/filters.py:199
#: taiga/base/filters.py:255
msgid "'project' must be an integer value."
msgstr ""
#: taiga/base/tags.py:25 #: taiga/base/tags.py:25
msgid "tags" msgid "tags"
msgstr "" msgstr ""
@ -166,6 +299,10 @@ msgid ""
" " " "
msgstr "" msgstr ""
#: taiga/base/utils/signals.py:27
msgid "The parameters must be lists of at least one parameter (the signal)."
msgstr ""
#: taiga/export_import/api.py:183 #: taiga/export_import/api.py:183
msgid "Needed dump file" msgid "Needed dump file"
msgstr "" msgstr ""
@ -174,16 +311,80 @@ msgstr ""
msgid "Invalid dump format" msgid "Invalid dump format"
msgstr "" msgstr ""
#: taiga/export_import/serializers.py:377 #: taiga/export_import/dump_service.py:88
msgid "error importing project"
msgstr ""
#: taiga/export_import/dump_service.py:101
msgid "error importing choices"
msgstr ""
#: taiga/export_import/dump_service.py:106
msgid "error importing default choices"
msgstr ""
#: taiga/export_import/dump_service.py:116
msgid "error importing custom attributes"
msgstr ""
#: taiga/export_import/dump_service.py:121
msgid "error importing roles"
msgstr ""
#: taiga/export_import/dump_service.py:136
msgid "error importing memberships"
msgstr ""
#: taiga/export_import/dump_service.py:141
msgid "error importing milestones"
msgstr ""
#: taiga/export_import/dump_service.py:146
msgid "error importing wiki pages"
msgstr ""
#: taiga/export_import/dump_service.py:151
msgid "error importing wiki links"
msgstr ""
#: taiga/export_import/dump_service.py:156
#: taiga/export_import/dump_service.py:166
msgid "error importing issues"
msgstr ""
#: taiga/export_import/dump_service.py:161
msgid "error importing user stories"
msgstr ""
#: taiga/export_import/serializers.py:157
msgid "{}=\"{}\" not found in this project"
msgstr ""
#: taiga/export_import/serializers.py:378
#: taiga/projects/custom_attributes/serializers.py:104 #: taiga/projects/custom_attributes/serializers.py:104
msgid "Invalid content. It must be {\"key\": \"value\",...}" msgid "Invalid content. It must be {\"key\": \"value\",...}"
msgstr "" msgstr ""
#: taiga/export_import/serializers.py:392 #: taiga/export_import/serializers.py:393
#: taiga/projects/custom_attributes/serializers.py:119 #: taiga/projects/custom_attributes/serializers.py:119
msgid "It contain invalid custom fields." msgid "It contain invalid custom fields."
msgstr "" msgstr ""
#: taiga/export_import/serializers.py:462
#: taiga/projects/milestones/serializers.py:63
#: taiga/projects/serializers.py:64 taiga/projects/serializers.py:88
#: taiga/projects/serializers.py:110 taiga/projects/serializers.py:142
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-subject.jinja:1 #: taiga/export_import/templates/emails/dump_project-subject.jinja:1
#, python-format #, python-format
msgid "[%(project)s] Your project dump has been generated" msgid "[%(project)s] Your project dump has been generated"
@ -286,28 +487,55 @@ msgstr ""
msgid "The payload is not a valid application/x-www-form-urlencoded" msgid "The payload is not a valid application/x-www-form-urlencoded"
msgstr "" msgstr ""
#: taiga/hooks/bitbucket/event_hooks.py:46 #: taiga/hooks/bitbucket/event_hooks.py:45
msgid "The payload is not valid" msgid "The payload is not valid"
msgstr "" msgstr ""
#: taiga/hooks/bitbucket/event_hooks.py:82 #: taiga/hooks/bitbucket/event_hooks.py:81
#: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:74 #: taiga/hooks/github/event_hooks.py:75 taiga/hooks/gitlab/event_hooks.py:74
msgid "The referenced element doesn't exist" msgid "The referenced element doesn't exist"
msgstr "" msgstr ""
#: taiga/hooks/bitbucket/event_hooks.py:89 #: taiga/hooks/bitbucket/event_hooks.py:88
#: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:81 #: taiga/hooks/github/event_hooks.py:82 taiga/hooks/gitlab/event_hooks.py:81
msgid "The status doesn't exist" msgid "The status doesn't exist"
msgstr "" msgstr ""
#: taiga/hooks/bitbucket/event_hooks.py:94
msgid "Status changed from BitBucket commit"
msgstr ""
#: taiga/hooks/github/event_hooks.py:88
msgid "Status changed from GitHub commit"
msgstr ""
#: taiga/hooks/github/event_hooks.py:113 taiga/hooks/gitlab/event_hooks.py:114 #: taiga/hooks/github/event_hooks.py:113 taiga/hooks/gitlab/event_hooks.py:114
msgid "Invalid issue information" msgid "Invalid issue information"
msgstr "" msgstr ""
#: taiga/hooks/github/event_hooks.py:128
msgid "Created from GitHub"
msgstr ""
#: taiga/hooks/github/event_hooks.py:135 taiga/hooks/github/event_hooks.py:144 #: taiga/hooks/github/event_hooks.py:135 taiga/hooks/github/event_hooks.py:144
msgid "Invalid issue comment information" msgid "Invalid issue comment information"
msgstr "" msgstr ""
#: taiga/hooks/github/event_hooks.py:152
msgid ""
"From GitHub:\n"
"\n"
"{}"
msgstr ""
#: taiga/hooks/gitlab/event_hooks.py:87
msgid "Status changed from GitLab commit"
msgstr ""
#: taiga/hooks/gitlab/event_hooks.py:129
msgid "Created from GitLab"
msgstr ""
#: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31 #: taiga/permissions/permissions.py:21 taiga/permissions/permissions.py:31
#: taiga/permissions/permissions.py:52 #: taiga/permissions/permissions.py:52
msgid "View project" msgid "View project"
@ -470,6 +698,14 @@ msgstr ""
msgid "Admin roles" msgid "Admin roles"
msgstr "" msgstr ""
#: taiga/projects/api.py:189
msgid "Not valid template name"
msgstr ""
#: taiga/projects/api.py:192
msgid "Not valid template description"
msgstr ""
#: taiga/projects/api.py:454 taiga/projects/serializers.py:227 #: taiga/projects/api.py:454 taiga/projects/serializers.py:227
msgid "At least one of the user must be an active admin" msgid "At least one of the user must be an active admin"
msgstr "" msgstr ""
@ -478,10 +714,19 @@ msgstr ""
msgid "You don't have permisions to see that." msgid "You don't have permisions to see that."
msgstr "" 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:54 taiga/projects/issues/models.py:38 #: taiga/projects/attachments/models.py:54 taiga/projects/issues/models.py:38
#: taiga/projects/milestones/models.py:39 taiga/projects/models.py:131 #: taiga/projects/milestones/models.py:39 taiga/projects/models.py:131
#: taiga/projects/tasks/models.py:37 taiga/projects/userstories/models.py:64 #: taiga/projects/notifications/models.py:57 taiga/projects/tasks/models.py:37
#: taiga/projects/wiki/models.py:34 taiga/userstorage/models.py:25 #: taiga/projects/userstories/models.py:64 taiga/projects/wiki/models.py:34
#: taiga/userstorage/models.py:25
msgid "owner" msgid "owner"
msgstr "" msgstr ""
@ -492,9 +737,9 @@ msgstr ""
#: taiga/projects/models.py:383 taiga/projects/models.py:412 #: taiga/projects/models.py:383 taiga/projects/models.py:412
#: taiga/projects/models.py:445 taiga/projects/models.py:468 #: taiga/projects/models.py:445 taiga/projects/models.py:468
#: taiga/projects/models.py:495 taiga/projects/models.py:526 #: taiga/projects/models.py:495 taiga/projects/models.py:526
#: taiga/projects/tasks/models.py:41 taiga/projects/userstories/models.py:62 #: taiga/projects/notifications/models.py:69 taiga/projects/tasks/models.py:41
#: taiga/projects/wiki/models.py:28 taiga/projects/wiki/models.py:66 #: taiga/projects/userstories/models.py:62 taiga/projects/wiki/models.py:28
#: taiga/users/models.py:193 #: taiga/projects/wiki/models.py:66 taiga/users/models.py:193
msgid "project" msgid "project"
msgstr "" msgstr ""
@ -542,6 +787,14 @@ msgstr ""
msgid "order" msgid "order"
msgstr "" msgstr ""
#: taiga/projects/choices.py:21
msgid "AppearIn"
msgstr ""
#: taiga/projects/choices.py:22
msgid "Talky"
msgstr ""
#: taiga/projects/custom_attributes/models.py:31 #: taiga/projects/custom_attributes/models.py:31
#: taiga/projects/milestones/models.py:34 taiga/projects/models.py:120 #: taiga/projects/milestones/models.py:34 taiga/projects/models.py:120
#: taiga/projects/models.py:338 taiga/projects/models.py:377 #: taiga/projects/models.py:338 taiga/projects/models.py:377
@ -573,6 +826,14 @@ msgstr ""
msgid "Already exists one with the same name." msgid "Already exists one with the same name."
msgstr "" 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 #: taiga/projects/history/choices.py:27
msgid "Change" msgid "Change"
msgstr "" msgstr ""
@ -637,6 +898,7 @@ msgstr ""
#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134 #: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:134
#: taiga/projects/history/templates/emails/includes/fields_diff-html.jinja:145 #: 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" msgid "Unassigned"
msgstr "" msgstr ""
@ -783,6 +1045,10 @@ msgstr ""
msgid "disponibility" msgid "disponibility"
msgstr "" 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 #: taiga/projects/milestones/validators.py:12
msgid "There's no sprint with that id" msgid "There's no sprint with that id"
msgstr "" msgstr ""
@ -984,14 +1250,27 @@ msgstr ""
msgid "watchers" msgid "watchers"
msgstr "" msgstr ""
#: taiga/projects/notifications/models.py:57 #: taiga/projects/notifications/models.py:59
msgid "created date time" msgid "created date time"
msgstr "" msgstr ""
#: taiga/projects/notifications/models.py:59 #: taiga/projects/notifications/models.py:61
msgid "updated date time" msgid "updated date time"
msgstr "" 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/wiki/wikipage-change-subject.jinja:1 #: taiga/projects/notifications/templates/emails/wiki/wikipage-change-subject.jinja:1
#, python-format #, python-format
msgid "" msgid ""
@ -1013,6 +1292,22 @@ msgid ""
"[%(project)s] Deleted the Wiki Page \"%(page)s\"\n" "[%(project)s] Deleted the Wiki Page \"%(page)s\"\n"
msgstr "" msgstr ""
#: taiga/projects/notifications/validators.py:43
msgid "Watchers contains invalid users"
msgstr ""
#: taiga/projects/occ/mixins.py:34
msgid "The version must be an integer"
msgstr ""
#: taiga/projects/occ/mixins.py:55
msgid "The version is not valid"
msgstr ""
#: taiga/projects/occ/mixins.py:71
msgid "The version doesn't match with the current one"
msgstr ""
#: taiga/projects/occ/mixins.py:91 #: taiga/projects/occ/mixins.py:91
msgid "version" msgid "version"
msgstr "" msgstr ""
@ -1029,6 +1324,10 @@ msgstr ""
msgid "Invalid role for the project" msgid "Invalid role for the project"
msgstr "" msgstr ""
#: taiga/projects/serializers.py:313
msgid "Total milestones must be major or equal to zero"
msgstr ""
#: taiga/projects/serializers.py:370 #: taiga/projects/serializers.py:370
msgid "Default options" msgid "Default options"
msgstr "" msgstr ""
@ -1065,6 +1364,14 @@ msgstr ""
msgid "Roles" msgid "Roles"
msgstr "" 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:57 taiga/projects/tasks/api.py:60 #: taiga/projects/tasks/api.py:57 taiga/projects/tasks/api.py:60
#: taiga/projects/tasks/api.py:63 taiga/projects/tasks/api.py:66 #: taiga/projects/tasks/api.py:63 taiga/projects/tasks/api.py:66
msgid "You don't have permissions for add/modify this task." msgid "You don't have permissions for add/modify this task."
@ -1189,15 +1496,15 @@ msgstr ""
msgid "There's no user story with that id" msgid "There's no user story with that id"
msgstr "" msgstr ""
#: taiga/projects/validators.py:12 #: taiga/projects/validators.py:28
msgid "There's no project with that id" msgid "There's no project with that id"
msgstr "" msgstr ""
#: taiga/projects/validators.py:21 #: taiga/projects/validators.py:37
msgid "There's no user story status with that id" msgid "There's no user story status with that id"
msgstr "" msgstr ""
#: taiga/projects/validators.py:30 #: taiga/projects/validators.py:46
msgid "There's no task status with that id" msgid "There's no task status with that id"
msgstr "" msgstr ""
@ -1286,12 +1593,12 @@ msgstr ""
msgid "Not valid email" msgid "Not valid email"
msgstr "" msgstr ""
#: taiga/users/api.py:246 taiga/users/api.py:252 #: taiga/users/api.py:247 taiga/users/api.py:253
msgid "" msgid ""
"Invalid, are you sure the token is correct and you didn't use it before?" "Invalid, are you sure the token is correct and you didn't use it before?"
msgstr "" msgstr ""
#: taiga/users/api.py:279 taiga/users/api.py:287 taiga/users/api.py:290 #: taiga/users/api.py:280 taiga/users/api.py:288 taiga/users/api.py:291
msgid "Invalid, are you sure the token is correct?" msgid "Invalid, are you sure the token is correct?"
msgstr "" msgstr ""
@ -1364,23 +1671,18 @@ msgstr ""
msgid "permissions" msgid "permissions"
msgstr "" msgstr ""
#: taiga/users/serializers.py:52
msgid "invalid username"
msgstr ""
#: taiga/users/serializers.py:53 #: taiga/users/serializers.py:53
msgid "invalid" msgid "invalid"
msgstr "" msgstr ""
#: taiga/users/serializers.py:58
msgid ""
"Required. 255 characters or fewer. Letters, numbers and /./-/_ characters'"
msgstr ""
#: taiga/users/serializers.py:64 #: taiga/users/serializers.py:64
msgid "Invalid username. Try with a different one." 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 ""
#: taiga/users/templates/emails/change_email-body-html.jinja:4 #: taiga/users/templates/emails/change_email-body-html.jinja:4
#, python-format #, python-format
msgid "" msgid ""
@ -1507,25 +1809,25 @@ msgid "secret key"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:39 #: taiga/webhooks/models.py:39
msgid "Status code" msgid "status code"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:40 #: taiga/webhooks/models.py:40
msgid "Request data" msgid "request data"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:41 #: taiga/webhooks/models.py:41
msgid "Request headers" msgid "request headers"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:42 #: taiga/webhooks/models.py:42
msgid "Response data" msgid "response data"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:43 #: taiga/webhooks/models.py:43
msgid "Response headers" msgid "response headers"
msgstr "" msgstr ""
#: taiga/webhooks/models.py:44 #: taiga/webhooks/models.py:44
msgid "Duration" msgid "duration"
msgstr "" msgstr ""

View File

@ -18,7 +18,7 @@ import uuid
from django.db.models import signals from django.db.models import signals
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base import filters from taiga.base import filters
from taiga.base import response from taiga.base import response
@ -186,10 +186,10 @@ class ProjectViewSet(ModelCrudViewSet):
template_description = request.DATA.get('template_description', None) template_description = request.DATA.get('template_description', None)
if not template_name: if not template_name:
raise response.BadRequest("Not valid template name") raise response.BadRequest(_("Not valid template name"))
if not template_description: if not template_description:
raise response.BadRequest("Not valid template description") raise response.BadRequest(_("Not valid template description"))
template_slug = slugify_uniquely(template_name, models.ProjectTemplate) template_slug = slugify_uniquely(template_name, models.ProjectTemplate)

View File

@ -14,24 +14,18 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import os.path as path import os.path as path
import hashlib
import mimetypes import mimetypes
mimetypes.init() mimetypes.init()
from django.utils.translation import ugettext as _
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django import http
from taiga.base import filters from taiga.base import filters
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base.api import generics
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet
from taiga.base.api.utils import get_object_or_404 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
from taiga.projects.history.mixins import HistoryResourceMixin from taiga.projects.history.mixins import HistoryResourceMixin
@ -50,7 +44,7 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCru
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
partial = kwargs.get("partial", False) partial = kwargs.get("partial", False)
if not partial: if not partial:
raise exc.NotSupported("Non partial updates not supported") raise exc.NotSupported(_("Non partial updates not supported"))
return super().update(*args, **kwargs) return super().update(*args, **kwargs)
def get_content_type(self): def get_content_type(self):
@ -65,7 +59,7 @@ class BaseAttachmentViewSet(HistoryResourceMixin, WatchedResourceMixin, ModelCru
obj.name = path.basename(obj.attached_file.name).lower() obj.name = path.basename(obj.attached_file.name).lower()
if obj.project_id != obj.content_object.project_id: if obj.project_id != obj.content_object.project_id:
raise exc.WrongArguments("Project ID not matches between object and project") raise exc.WrongArguments(_("Project ID not matches between object and project"))
super().pre_save(obj) super().pre_save(obj)

View File

@ -14,7 +14,10 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _
VIDEOCONFERENCES_CHOICES = ( VIDEOCONFERENCES_CHOICES = (
("appear-in", "AppearIn"), ("appear-in", _("AppearIn")),
("talky", "Talky"), ("talky", _("Talky")),
) )

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _
from django.utils import timezone from django.utils import timezone
from taiga.base import response from taiga.base import response
@ -66,7 +67,7 @@ class HistoryViewSet(ReadOnlyListViewSet):
return response.NotFound() return response.NotFound()
if comment.delete_comment_date or comment.delete_comment_user: if comment.delete_comment_date or comment.delete_comment_user:
return response.BadRequest({"error": "Comment already deleted"}) return response.BadRequest({"error": _("Comment already deleted")})
comment.delete_comment_date = timezone.now() comment.delete_comment_date = timezone.now()
comment.delete_comment_user = {"pk": request.user.pk, "name": request.user.get_full_name()} comment.delete_comment_user = {"pk": request.user.pk, "name": request.user.get_full_name()}
@ -85,7 +86,7 @@ class HistoryViewSet(ReadOnlyListViewSet):
return response.NotFound() return response.NotFound()
if not comment.delete_comment_date and not comment.delete_comment_user: if not comment.delete_comment_date and not comment.delete_comment_user:
return response.BadRequest({"error": "Comment not deleted"}) return response.BadRequest({"error": _("Comment not deleted")})
comment.delete_comment_date = None comment.delete_comment_date = None
comment.delete_comment_user = None comment.delete_comment_user = None

View File

@ -33,7 +33,7 @@ class HistoryResourceMixin(object):
def get_last_history(self): def get_last_history(self):
if not self.__object_saved: if not self.__object_saved:
message = ("get_last_history() function called before any object are saved. " message = ("get_last_history() function called before any object are saved. "
"Seems you have a wrong mixing order on your resource.") "Seems you have a wrong mixing order on your resource.")
warnings.warn(message, RuntimeWarning) warnings.warn(message, RuntimeWarning)
return self.__last_history return self.__last_history

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.db.models import Q from django.db.models import Q
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse

View File

@ -72,7 +72,7 @@ class Milestone(WatchedModelMixin, models.Model):
def clean(self): def clean(self):
# Don't allow draft entries to have a pub_date. # Don't allow draft entries to have a pub_date.
if self.estimated_start and self.estimated_finish and self.estimated_start > self.estimated_finish: if self.estimated_start and self.estimated_finish and self.estimated_start > self.estimated_finish:
raise ValidationError('The estimated start must be previous to the estimated finish.') raise ValidationError(_('The estimated start must be previous to the estimated finish.'))
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self._importing or not self.modified_date: if not self._importing or not self.modified_date:

View File

@ -14,15 +14,16 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
from taiga.base.utils import json
from ..userstories.serializers import UserStorySerializer from ..userstories.serializers import UserStorySerializer
from . import models from . import models
class MilestoneSerializer(serializers.ModelSerializer): class MilestoneSerializer(serializers.ModelSerializer):
user_stories = UserStorySerializer(many=True, required=False, read_only=True) user_stories = UserStorySerializer(many=True, required=False, read_only=True)
total_points = serializers.SerializerMethodField("get_total_points") total_points = serializers.SerializerMethodField("get_total_points")
@ -59,6 +60,6 @@ class MilestoneSerializer(serializers.ModelSerializer):
qs = models.Milestone.objects.filter(project=attrs["project"], name=attrs[source]) qs = models.Milestone.objects.filter(project=attrs["project"], name=attrs[source])
if qs and qs.exists(): if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base import response from taiga.base import response
from taiga.base import exceptions as exc from taiga.base import exceptions as exc

View File

@ -18,9 +18,11 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone from django.utils import timezone
from .choices import NOTIFY_LEVEL_CHOICES
from taiga.projects.history.choices import HISTORY_TYPE_CHOICES from taiga.projects.history.choices import HISTORY_TYPE_CHOICES
from .choices import NOTIFY_LEVEL_CHOICES
class NotifyPolicy(models.Model): class NotifyPolicy(models.Model):
""" """
This class represents a persistence for This class represents a persistence for
@ -52,19 +54,19 @@ class HistoryChangeNotification(models.Model):
""" """
key = models.CharField(max_length=255, unique=False, editable=False) key = models.CharField(max_length=255, unique=False, editable=False)
owner = models.ForeignKey("users.User", null=False, blank=False, owner = models.ForeignKey("users.User", null=False, blank=False,
verbose_name="owner",related_name="+") verbose_name=_("owner"), related_name="+")
created_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True, created_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True,
verbose_name=_("created date time")) verbose_name=_("created date time"))
updated_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True, updated_datetime = models.DateTimeField(null=False, blank=False, auto_now_add=True,
verbose_name=_("updated date time")) verbose_name=_("updated date time"))
history_entries = models.ManyToManyField("history.HistoryEntry", null=True, blank=True, history_entries = models.ManyToManyField("history.HistoryEntry", null=True, blank=True,
verbose_name="history entries", verbose_name=_("history entries"),
related_name="+") related_name="+")
notify_users = models.ManyToManyField("users.User", null=True, blank=True, notify_users = models.ManyToManyField("users.User", null=True, blank=True,
verbose_name="notify users", verbose_name=_("notify users"),
related_name="+") related_name="+")
project = models.ForeignKey("projects.Project", null=False, blank=False, project = models.ForeignKey("projects.Project", null=False, blank=False,
verbose_name="project",related_name="+") verbose_name=_("project"),related_name="+")
history_type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES) history_type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES)

View File

@ -22,6 +22,7 @@ from django.contrib.contenttypes.models import ContentType
from django.utils import timezone from django.utils import timezone
from django.db import transaction from django.db import transaction
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _
from djmail import template_mail from djmail import template_mail
@ -37,6 +38,7 @@ from taiga.users.models import User
from .models import HistoryChangeNotification from .models import HistoryChangeNotification
def notify_policy_exists(project, user) -> bool: def notify_policy_exists(project, user) -> bool:
""" """
Check if policy exists for specified project Check if policy exists for specified project
@ -58,7 +60,7 @@ def create_notify_policy(project, user, level=NotifyLevel.notwatch):
user=user, user=user,
notify_level=level) notify_level=level)
except IntegrityError as e: except IntegrityError as e:
raise exc.IntegrityError("Notify exists for specified user and project") from e 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.notwatch):
@ -72,7 +74,7 @@ def create_notify_policy_if_not_exists(project, user, level=NotifyLevel.notwatch
defaults={"notify_level": level}) defaults={"notify_level": level})
return result[0] return result[0]
except IntegrityError as e: except IntegrityError as e:
raise exc.IntegrityError("Notify exists for specified user and project") from e raise exc.IntegrityError(_("Notify exists for specified user and project")) from e
def get_notify_policy(project, user): def get_notify_policy(project, user):
@ -256,8 +258,7 @@ def send_sync_notifications(notification_id):
obj, _ = get_last_snapshot_for_key(notification.key) obj, _ = get_last_snapshot_for_key(notification.key)
obj_class = get_model_from_key(obj.key) obj_class = get_model_from_key(obj.key)
context = { context = {"obj_class": obj_class,
"obj_class": obj_class,
"snapshot": obj.snapshot, "snapshot": obj.snapshot,
"project": notification.project, "project": notification.project,
"changer": notification.owner, "changer": notification.owner,

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
@ -40,6 +40,6 @@ class WatchersValidator:
# in project members list # in project members list
result = set(users).difference(set(project.members.all())) result = set(users).difference(set(project.members.all()))
if result: if result:
raise serializers.ValidationError("Watchers contains invalid users") raise serializers.ValidationError(_("Watchers contains invalid users"))
return attrs return attrs

View File

@ -31,7 +31,7 @@ class OCCResourceMixin(object):
try: try:
param_version = param_version and int(param_version) param_version = param_version and int(param_version)
except (ValueError, TypeError): except (ValueError, TypeError):
raise exc.WrongArguments({"version": "The version must be an integer"}) raise exc.WrongArguments({"version": _("The version must be an integer")})
return param_version return param_version
@ -52,7 +52,7 @@ class OCCResourceMixin(object):
# Extract param version # Extract param version
param_version = self._extract_param_version() param_version = self._extract_param_version()
if not self._validate_param_version(param_version, current_version): if not self._validate_param_version(param_version, current_version):
raise exc.WrongArguments({"version": "The version is not valid"}) raise exc.WrongArguments({"version": _("The version is not valid")})
if current_version != param_version: if current_version != param_version:
diff_versions = current_version - param_version diff_versions = current_version - param_version
@ -68,7 +68,7 @@ class OCCResourceMixin(object):
both_modified = modifying_fields & modified_fields both_modified = modifying_fields & modified_fields
if both_modified: if both_modified:
raise exc.WrongArguments({"version": "The version doesn't match with the current one"}) raise exc.WrongArguments({"version": _("The version doesn't match with the current one")})
if obj.id: if obj.id:
obj.version = models.F('version') + 1 obj.version = models.F('version') + 1

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base.api.permissions import TaigaResourcePermission from taiga.base.api.permissions import TaigaResourcePermission
from taiga.base.api.permissions import HasProjectPerm from taiga.base.api.permissions import HasProjectPerm

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.db.models import Q from django.db.models import Q
from rest_framework import serializers from rest_framework import serializers
@ -61,7 +61,7 @@ class PointsSerializer(ModelSerializer):
qs = models.Points.objects.filter(project=attrs["project"], name=attrs[source]) qs = models.Points.objects.filter(project=attrs["project"], name=attrs[source])
if qs and qs.exists(): if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs
@ -85,7 +85,7 @@ class UserStoryStatusSerializer(ModelSerializer):
name=attrs[source]) name=attrs[source])
if qs and qs.exists(): if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs
@ -107,7 +107,7 @@ class TaskStatusSerializer(ModelSerializer):
qs = models.TaskStatus.objects.filter(project=attrs["project"], name=attrs[source]) qs = models.TaskStatus.objects.filter(project=attrs["project"], name=attrs[source])
if qs and qs.exists(): if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs
@ -139,7 +139,7 @@ class IssueStatusSerializer(ModelSerializer):
qs = models.IssueStatus.objects.filter(project=attrs["project"], name=attrs[source]) qs = models.IssueStatus.objects.filter(project=attrs["project"], name=attrs[source])
if qs and qs.exists(): if qs and qs.exists():
raise serializers.ValidationError("Name duplicated for the project") raise serializers.ValidationError(_("Name duplicated for the project"))
return attrs return attrs
@ -310,7 +310,7 @@ class ProjectSerializer(ModelSerializer):
""" """
value = attrs[source] value = attrs[source]
if value is None: if value is None:
raise serializers.ValidationError("Total milestones must be major or equal to zero") raise serializers.ValidationError(_("Total milestones must be major or equal to zero"))
return attrs return attrs

View File

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext as _
from django.db.models import Q, Count from django.db.models import Q, Count
from django.apps import apps from django.apps import apps
import datetime import datetime
@ -21,6 +22,7 @@ import copy
from taiga.projects.history.models import HistoryEntry from taiga.projects.history.models import HistoryEntry
def _get_milestones_stats_for_backlog(project): def _get_milestones_stats_for_backlog(project):
""" """
Get collection of stats for each millestone of project. Get collection of stats for each millestone of project.
@ -67,7 +69,7 @@ def _get_milestones_stats_for_backlog(project):
current_client_increment += sum(ml.client_increment_points.values()) current_client_increment += sum(ml.client_increment_points.values())
else: else:
milestone_name = "Future sprint" milestone_name = _("Future sprint")
team_increment = current_team_increment + future_team_increment, team_increment = current_team_increment + future_team_increment,
client_increment = current_client_increment + future_client_increment, client_increment = current_client_increment + future_client_increment,
current_evolution = None current_evolution = None
@ -84,7 +86,7 @@ def _get_milestones_stats_for_backlog(project):
evolution = (project.total_story_points - current_evolution evolution = (project.total_story_points - current_evolution
if current_evolution is not None and project.total_story_points else None) if current_evolution is not None and project.total_story_points else None)
yield { yield {
'name': 'Project End', 'name': _('Project End'),
'optimal': optimal_points, 'optimal': optimal_points,
'evolution': evolution, 'evolution': evolution,
'team-increment': team_increment, 'team-increment': team_increment,
@ -119,8 +121,8 @@ def _count_owned_object(user_obj, counting_storage):
else: else:
counting_storage[0] = {} counting_storage[0] = {}
counting_storage[0]['count'] = 1 counting_storage[0]['count'] = 1
counting_storage[0]['username'] = 'Unassigned' counting_storage[0]['username'] = _('Unassigned')
counting_storage[0]['name'] = 'Unassigned' counting_storage[0]['name'] = _('Unassigned')
counting_storage[0]['id'] = 0 counting_storage[0]['id'] = 0
counting_storage[0]['color'] = 'black' counting_storage[0]['color'] = 'black'

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from taiga.base.api.utils import get_object_or_404 from taiga.base.api.utils import get_object_or_404
from taiga.base import filters, response from taiga.base import filters, response

View File

@ -1,4 +1,4 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -14,9 +14,9 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from taiga.base.api import serializers
from . import models from . import models

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated

View File

@ -18,7 +18,7 @@ import uuid
from django.apps import apps from django.apps import apps
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.core.validators import validate_email from django.core.validators import validate_email
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.conf import settings from django.conf import settings

View File

@ -21,6 +21,7 @@ This model contains a domain logic for users application.
from django.apps import apps from django.apps import apps
from django.db.models import Q from django.db.models import Q
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _
from easy_thumbnails.files import get_thumbnailer from easy_thumbnails.files import get_thumbnailer
from easy_thumbnails.exceptions import InvalidImageFormatError from easy_thumbnails.exceptions import InvalidImageFormatError
@ -44,11 +45,11 @@ def get_and_validate_user(*, username:str, password:str) -> bool:
qs = user_model.objects.filter(Q(username=username) | qs = user_model.objects.filter(Q(username=username) |
Q(email=username)) Q(email=username))
if len(qs) == 0: if len(qs) == 0:
raise exc.WrongArguments("Username or password does not matches user.") raise exc.WrongArguments(_("Username or password does not matches user."))
user = qs[0] user = qs[0]
if not user.check_password(password): if not user.check_password(password):
raise exc.WrongArguments("Username or password does not matches user.") raise exc.WrongArguments(_("Username or password does not matches user."))
return user return user
@ -80,6 +81,10 @@ def get_big_photo_url(photo):
def get_big_photo_or_gravatar_url(user): def get_big_photo_or_gravatar_url(user):
"""Get the user's big photo/gravatar url.""" """Get the user's big photo/gravatar url."""
if user: if not user:
return get_big_photo_url(user.photo) if user.photo else get_gravatar_url(user.email, size=settings.DEFAULT_BIG_AVATAR_SIZE) return ""
return ""
if user.photo:
return get_big_photo_url(user.photo)
else:
return get_gravatar_url(user.email, size=settings.DEFAULT_BIG_AVATAR_SIZE)

View File

@ -15,7 +15,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext as _
from django.db import IntegrityError from django.db import IntegrityError
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet

View File

@ -36,12 +36,12 @@ class WebhookLog(models.Model):
webhook = models.ForeignKey(Webhook, null=False, blank=False, webhook = models.ForeignKey(Webhook, null=False, blank=False,
related_name="logs") related_name="logs")
url = models.URLField(null=False, blank=False, verbose_name=_("URL")) url = models.URLField(null=False, blank=False, verbose_name=_("URL"))
status = models.IntegerField(null=False, blank=False, verbose_name=_("Status code")) status = models.IntegerField(null=False, blank=False, verbose_name=_("status code"))
request_data = JsonField(null=False, blank=False, verbose_name=_("Request data")) request_data = JsonField(null=False, blank=False, verbose_name=_("request data"))
request_headers = JsonField(null=False, blank=False, verbose_name=_("Request headers"), default={}) request_headers = JsonField(null=False, blank=False, verbose_name=_("request headers"), default={})
response_data = models.TextField(null=False, blank=False, verbose_name=_("Response data")) response_data = models.TextField(null=False, blank=False, verbose_name=_("response data"))
response_headers = JsonField(null=False, blank=False, verbose_name=_("Response headers"), default={}) response_headers = JsonField(null=False, blank=False, verbose_name=_("response headers"), default={})
duration = models.FloatField(null=False, blank=False, verbose_name=_("Duration"), default=0) duration = models.FloatField(null=False, blank=False, verbose_name=_("duration"), default=0)
created = models.DateTimeField(auto_now_add=True) created = models.DateTimeField(auto_now_add=True)
class Meta: class Meta: