Merge pull request #266 from taigaio/more-flake8-fixes

Some flake8 fixes
remotes/origin/enhancement/email-actions
David Barragán Merino 2015-02-24 19:55:27 +01:00
commit 3dc143149f
56 changed files with 108 additions and 109 deletions

View File

@ -27,8 +27,6 @@ from taiga.base.decorators import list_route
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base import response from taiga.base import response
from taiga.users.services import get_and_validate_user
from .serializers import PublicRegisterSerializer from .serializers import PublicRegisterSerializer
from .serializers import PrivateRegisterForExistingUserSerializer from .serializers import PrivateRegisterForExistingUserSerializer
from .serializers import PrivateRegisterForNewUserSerializer from .serializers import PrivateRegisterForNewUserSerializer

View File

@ -32,7 +32,6 @@ selfcontained tokens. This trust tokes from external
fraudulent modifications. fraudulent modifications.
""" """
import base64
import re import re
from django.conf import settings from django.conf import settings
@ -40,6 +39,7 @@ from rest_framework.authentication import BaseAuthentication
from .tokens import get_user_for_token from .tokens import get_user_for_token
class Session(BaseAuthentication): class Session(BaseAuthentication):
""" """
Session based authentication like the standard Session based authentication like the standard
@ -82,8 +82,8 @@ class Token(BaseAuthentication):
token = token_rx_match.group(1) token = token_rx_match.group(1)
max_age_auth_token = getattr(settings, "MAX_AGE_AUTH_TOKEN", None) max_age_auth_token = getattr(settings, "MAX_AGE_AUTH_TOKEN", None)
user = get_user_for_token(token, "authentication", user = get_user_for_token(token, "authentication",
max_age=max_age_auth_token) max_age=max_age_auth_token)
return (user, token) return (user, token)
def authenticate_header(self, request): def authenticate_header(self, request):

View File

@ -19,12 +19,13 @@ 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
def get_token_for_user(user, scope): def get_token_for_user(user, scope):
""" """
Generate a new signed token containing Generate a new signed token containing
a specified user limited for a scope (identified as a string). a specified user limited for a scope (identified as a string).
""" """
data = {"user_%s_id"%(scope): user.id} data = {"user_%s_id" % (scope): user.id}
return signing.dumps(data) return signing.dumps(data)
@ -47,7 +48,7 @@ def get_user_for_token(token, scope, max_age=None):
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:

View File

@ -19,13 +19,11 @@
import warnings import warnings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied from django.core.exceptions import ImproperlyConfigured
from django.core.paginator import Paginator, InvalidPage from django.core.paginator import Paginator, InvalidPage
from django.http import Http404 from django.http import Http404
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from rest_framework import exceptions
from rest_framework.request import clone_request
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from . import views from . import views
@ -166,8 +164,8 @@ class GenericAPIView(views.APIView):
page = paginator.page(page_number) page = paginator.page(page_number)
except InvalidPage as e: except InvalidPage as e:
raise Http404(_('Invalid page (%(page_number)s): %(message)s') % { raise Http404(_('Invalid page (%(page_number)s): %(message)s') % {
'page_number': page_number, 'page_number': page_number,
'message': str(e) 'message': str(e)
}) })
if deprecated_style: if deprecated_style:
@ -193,16 +191,16 @@ 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 RuntimeException('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
###########################################################
######################## # The following methods provide default implementations #
### The following methods provide default implementations # that you may want to override for more complex cases. #
### that you may want to override for more complex cases. ###########################################################
def get_paginate_by(self, queryset=None): def get_paginate_by(self, queryset=None):
""" """
@ -214,8 +212,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 RuntimeException('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(
@ -263,8 +261,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'" raise ImproperlyConfigured("'%s' must define 'queryset' or 'model'" % self.__class__.__name__)
% self.__class__.__name__)
def get_object(self, queryset=None): def get_object(self, queryset=None):
""" """
@ -280,7 +277,7 @@ class GenericAPIView(views.APIView):
else: else:
# NOTE: explicit exception for avoid and fix # NOTE: explicit exception for avoid and fix
# usage of deprecated way of get_object # usage of deprecated way of get_object
raise RuntimeException("DEPRECATED") raise RuntimeError("DEPRECATED")
# Perform the lookup filtering. # Perform the lookup filtering.
# Note that `pk` and `slug` are deprecated styles of lookup filtering. # Note that `pk` and `slug` are deprecated styles of lookup filtering.
@ -292,11 +289,11 @@ 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 RuntimeException('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 RuntimeException('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 '
@ -314,12 +311,13 @@ class GenericAPIView(views.APIView):
except Http404: except Http404:
return None return None
######################## ###################################################
### The following are placeholder methods, # The following are placeholder methods, #
### and are intended to be overridden. # and are intended to be overridden. #
### # #
### The are not called by GenericAPIView directly, # The are not called by GenericAPIView directly, #
### but are used by the mixin methods. # but are used by the mixin methods. #
###################################################
def pre_conditions_on_save(self, obj): def pre_conditions_on_save(self, obj):
""" """
@ -363,11 +361,11 @@ class GenericAPIView(views.APIView):
pass pass
########################################################## ######################################################
### Concrete view classes that provide method handlers ### # Concrete view classes that provide method handlers #
### by composing the mixin classes with the base view. ### # by composing the mixin classes with the base view. #
### NOTE: not used by taiga. ### # NOTE: not used by taiga. #
########################################################## ######################################################
class CreateAPIView(mixins.CreateModelMixin, class CreateAPIView(mixins.CreateModelMixin,
GenericAPIView): GenericAPIView):

View File

@ -219,8 +219,10 @@ class IsObjectOwner(PermissionComponent):
class AllowAnyPermission(ResourcePermission): class AllowAnyPermission(ResourcePermission):
enought_perms = AllowAny() enought_perms = AllowAny()
class IsAuthenticatedPermission(ResourcePermission): class IsAuthenticatedPermission(ResourcePermission):
enought_perms = IsAuthenticated() enought_perms = IsAuthenticated()
class TaigaResourcePermission(ResourcePermission): class TaigaResourcePermission(ResourcePermission):
enought_perms = IsSuperUser() enought_perms = IsSuperUser()

View File

@ -56,6 +56,7 @@ def get_view_name(view_cls, suffix=None):
return name return name
def get_view_description(view_cls, html=False): def get_view_description(view_cls, html=False):
""" """
Given a view class, return a textual description to represent the view. Given a view class, return a textual description to represent the view.
@ -141,7 +142,6 @@ class APIView(View):
headers['Vary'] = 'Accept' headers['Vary'] = 'Accept'
return headers return headers
def http_method_not_allowed(self, request, *args, **kwargs): def http_method_not_allowed(self, request, *args, **kwargs):
""" """
If `request.method` does not correspond to a handler method, If `request.method` does not correspond to a handler method,
@ -445,7 +445,7 @@ class APIView(View):
def api_server_error(request, *args, **kwargs): def api_server_error(request, *args, **kwargs):
if settings.DEBUG == 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

@ -124,6 +124,7 @@ class GenericViewSet(ViewSetMixin, generics.GenericAPIView):
""" """
pass pass
class ReadOnlyListViewSet(pagination.HeadersPaginationMixin, class ReadOnlyListViewSet(pagination.HeadersPaginationMixin,
pagination.ConditionalPaginationMixin, pagination.ConditionalPaginationMixin,
GenericViewSet): GenericViewSet):
@ -132,6 +133,7 @@ class ReadOnlyListViewSet(pagination.HeadersPaginationMixin,
""" """
pass pass
class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, class ReadOnlyModelViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin, mixins.ListModelMixin,
GenericViewSet): GenericViewSet):

View File

@ -20,6 +20,7 @@ import sys
from django.apps import AppConfig from django.apps import AppConfig
from . import monkey from . import monkey
class BaseAppConfig(AppConfig): class BaseAppConfig(AppConfig):
name = "taiga.base" name = "taiga.base"
verbose_name = "Base App Config" verbose_name = "Base App Config"
@ -28,4 +29,3 @@ class BaseAppConfig(AppConfig):
print("Monkey patching...", file=sys.stderr) print("Monkey patching...", file=sys.stderr)
monkey.patch_restframework() monkey.patch_restframework()
monkey.patch_serializer() monkey.patch_serializer()

View File

@ -18,6 +18,7 @@ from taiga.base.exceptions import BaseException
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
class ConnectorBaseException(BaseException): class ConnectorBaseException(BaseException):
status_code = 400 status_code = 400
default_detail = _("Connection error.") default_detail = _("Connection error.")

View File

@ -17,7 +17,7 @@
import warnings import warnings
## Rest Framework 2.4 backport some decorators. # Rest Framework 2.4 backport some decorators.
def detail_route(methods=['get'], **kwargs): def detail_route(methods=['get'], **kwargs):
""" """
@ -51,12 +51,14 @@ def link(**kwargs):
""" """
msg = 'link is pending deprecation. Use detail_route instead.' msg = 'link is pending deprecation. Use detail_route instead.'
warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
def decorator(func): def decorator(func):
func.bind_to_methods = ['get'] func.bind_to_methods = ['get']
func.detail = True func.detail = True
func.permission_classes = kwargs.get('permission_classes', []) func.permission_classes = kwargs.get('permission_classes', [])
func.kwargs = kwargs func.kwargs = kwargs
return func return func
return decorator return decorator
@ -66,10 +68,12 @@ def action(methods=['post'], **kwargs):
""" """
msg = 'action is pending deprecation. Use detail_route instead.' msg = 'action is pending deprecation. Use detail_route instead.'
warnings.warn(msg, PendingDeprecationWarning, stacklevel=2) warnings.warn(msg, PendingDeprecationWarning, stacklevel=2)
def decorator(func): def decorator(func):
func.bind_to_methods = methods func.bind_to_methods = methods
func.detail = True func.detail = True
func.permission_classes = kwargs.get('permission_classes', []) func.permission_classes = kwargs.get('permission_classes', [])
func.kwargs = kwargs func.kwargs = kwargs
return func return func
return decorator return decorator

View File

@ -23,7 +23,6 @@ from django.utils.translation import ugettext_lazy as _
from django.http import Http404 from django.http import Http404
from taiga.base import response from taiga.base import response
from taiga.base.utils.json import to_json
class BaseException(exceptions.APIException): class BaseException(exceptions.APIException):

View File

@ -102,7 +102,8 @@ class PermissionBasedFilterBackend(FilterBackend):
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
@ -190,7 +191,8 @@ class CanViewProjectObjFilterBackend(FilterBackend):
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

View File

@ -17,7 +17,7 @@
import datetime import datetime
from django.db.models.loading import get_model from django.db.models.loading import get_model
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
from django.utils import timezone from django.utils import timezone
from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail from djmail.template_mail import MagicMailBuilder, InlineCSSTemplateMail
@ -139,10 +139,10 @@ class Command(BaseCommand):
] ]
context = { context = {
"project": Project.objects.all().order_by("?").first(), "project": Project.objects.all().order_by("?").first(),
"changer": User.objects.all().order_by("?").first(), "changer": User.objects.all().order_by("?").first(),
"history_entries": HistoryEntry.objects.all().order_by("?")[0:5], "history_entries": HistoryEntry.objects.all().order_by("?")[0:5],
"user": User.objects.all().order_by("?").first(), "user": User.objects.all().order_by("?").first(),
} }
for notification_email in notification_emails: for notification_email in notification_emails:

View File

@ -14,8 +14,6 @@
# 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 import http from django import http
@ -33,7 +31,7 @@ COORS_EXPOSE_HEADERS = ["x-pagination-count", "x-paginated", "x-paginated-by",
class CoorsMiddleware(object): class CoorsMiddleware(object):
def _populate_response(self, response): def _populate_response(self, response):
response["Access-Control-Allow-Origin"] = COORS_ALLOWED_ORIGINS response["Access-Control-Allow-Origin"] = COORS_ALLOWED_ORIGINS
response["Access-Control-Allow-Methods"] = ",".join(COORS_ALLOWED_METHODS) response["Access-Control-Allow-Methods"] = ",".join(COORS_ALLOWED_METHODS)
response["Access-Control-Allow-Headers"] = ",".join(COORS_ALLOWED_HEADERS) 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)

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 __future__ import print_function from __future__ import print_function
import sys
def patch_serializer(): def patch_serializer():
from rest_framework import serializers from rest_framework import serializers

View File

@ -15,10 +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 functools import partial
from collections import namedtuple from collections import namedtuple
from django.db.models import Q
from django.db import connection from django.db import connection
Neighbor = namedtuple("Neighbor", "left right") Neighbor = namedtuple("Neighbor", "left right")
@ -46,7 +44,7 @@ def get_neighbors(obj, results_set=None):
(SELECT "id" as id, ROW_NUMBER() OVER() (SELECT "id" as id, ROW_NUMBER() OVER()
FROM (%s) as ID_AND_ROW) FROM (%s) as ID_AND_ROW)
AS SELECTED_ID_AND_ROW AS SELECTED_ID_AND_ROW
"""%(base_sql) """ % (base_sql)
query += " WHERE id=%s;" query += " WHERE id=%s;"
params = list(base_params) + [obj.id] params = list(base_params) + [obj.id]

View File

@ -19,6 +19,7 @@ from django.core.files import storage
import django_sites as sites import django_sites as sites
class FileSystemStorage(storage.FileSystemStorage): class FileSystemStorage(storage.FileSystemStorage):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@ -15,9 +15,6 @@
# 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 re
from functools import partial
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _

View File

@ -31,6 +31,7 @@ def get_typename_for_model_class(model:object, for_concrete_model=True) -> str:
return "{0}.{1}".format(model._meta.app_label, model._meta.model_name) return "{0}.{1}".format(model._meta.app_label, model._meta.model_name)
def get_typename_for_model_instance(model_instance): def get_typename_for_model_instance(model_instance):
""" """
Get content type tuple from model instance. Get content type tuple from model instance.

View File

@ -16,6 +16,7 @@
import collections import collections
def dict_sum(*args): def dict_sum(*args):
result = collections.Counter() result = collections.Counter()
for arg in args: for arg in args:

View File

@ -15,6 +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/>.
def noop(*args, **kwargs): def noop(*args, **kwargs):
"""The noop function.""" """The noop function."""
return None return None

View File

@ -22,6 +22,7 @@ from django.utils.encoding import force_text
def dumps(data, ensure_ascii=True, encoder_class=encoders.JSONEncoder): def dumps(data, ensure_ascii=True, encoder_class=encoders.JSONEncoder):
return json.dumps(data, cls=encoder_class, indent=None, ensure_ascii=ensure_ascii) return json.dumps(data, cls=encoder_class, indent=None, ensure_ascii=ensure_ascii)
def loads(data): def loads(data):
if isinstance(data, bytes): if isinstance(data, bytes):
data = force_text(data) data = force_text(data)

View File

@ -20,6 +20,7 @@ def first(iterable):
return None return None
return iterable[0] return iterable[0]
def next(data:list): def next(data:list):
return data[1:] return data[1:]

View File

@ -36,4 +36,3 @@ def without_signals(*disablers):
for disabler in disablers: for disabler in disablers:
signal, *ids = disabler signal, *ids = disabler
signal.receivers = signal.backup_receivers signal.receivers = signal.backup_receivers

View File

@ -14,7 +14,6 @@
# 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 import baseconv
from django.template.defaultfilters import slugify as django_slugify from django.template.defaultfilters import slugify as django_slugify
import time import time

View File

@ -30,9 +30,9 @@ def get_current_session_id() -> str:
global _local global _local
if not hasattr(_local, "session_id"): if not hasattr(_local, "session_id"):
raise RuntimeException("No session identifier is found, " raise RuntimeError("No session identifier is found, "
"ara you sure that session id middleware " "are you sure that session id middleware "
"is active?") "is active?")
return _local.session_id return _local.session_id

View File

@ -26,8 +26,6 @@ from django.conf import settings
from django.core.files.storage import default_storage from django.core.files.storage import default_storage
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from rest_framework.decorators import throttle_classes
from taiga.base.decorators import detail_route, list_route from taiga.base.decorators import detail_route, list_route
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.base import response from taiga.base import response
@ -70,7 +68,7 @@ class ProjectExporterViewSet(mixins.ImportThrottlingPolicyMixin, GenericViewSet)
path = "exports/{}/{}-{}.json".format(project.pk, project.slug, uuid.uuid4().hex) path = "exports/{}/{}-{}.json".format(project.pk, project.slug, uuid.uuid4().hex)
content = ContentFile(ExportRenderer().render(service.project_to_dict(project), content = ContentFile(ExportRenderer().render(service.project_to_dict(project),
renderer_context={"indent": 4}).decode('utf-8')) renderer_context={"indent": 4}).decode('utf-8'))
default_storage.save(path, content) default_storage.save(path, content)
response_data = { response_data = {
@ -187,7 +185,6 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
response_data = ProjectSerializer(project).data response_data = ProjectSerializer(project).data
return response.Created(response_data) return response.Created(response_data)
@detail_route(methods=['post']) @detail_route(methods=['post'])
@method_decorator(atomic) @method_decorator(atomic)
def issue(self, request, *args, **kwargs): def issue(self, request, *args, **kwargs):
@ -195,7 +192,7 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
self.check_permissions(request, 'import_item', project) self.check_permissions(request, 'import_item', project)
signals.pre_save.disconnect(sender=Issue, signals.pre_save.disconnect(sender=Issue,
dispatch_uid="set_finished_date_when_edit_issue") dispatch_uid="set_finished_date_when_edit_issue")
issue = service.store_issue(project, request.DATA.copy()) issue = service.store_issue(project, request.DATA.copy())

View File

@ -14,8 +14,6 @@
# 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.db.models import signals
from taiga.projects.models import Membership from taiga.projects.models import Membership
from . import serializers from . import serializers

View File

@ -14,19 +14,19 @@
# 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.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand
from django.db import transaction from django.db import transaction
from django.db.models import signals from django.db.models import signals
from optparse import make_option from optparse import make_option
import json import json
import pprint
from taiga.projects.models import Project from taiga.projects.models import Project
from taiga.export_import.renderers import ExportRenderer from taiga.export_import.renderers import ExportRenderer
from taiga.export_import.dump_service import dict_to_project, TaigaImportError from taiga.export_import.dump_service import dict_to_project, TaigaImportError
from taiga.export_import.service import get_errors from taiga.export_import.service import get_errors
class Command(BaseCommand): class Command(BaseCommand):
args = '<dump_file> <owner-email>' args = '<dump_file> <owner-email>'
help = 'Export a project to json' help = 'Export a project to json'
@ -34,10 +34,10 @@ class Command(BaseCommand):
renderer = ExportRenderer() renderer = ExportRenderer()
option_list = BaseCommand.option_list + ( option_list = BaseCommand.option_list + (
make_option('--overwrite', make_option('--overwrite',
action='store_true', action='store_true',
dest='overwrite', dest='overwrite',
default=False, default=False,
help='Delete project if exists'), help='Delete project if exists'),
) )
def handle(self, *args, **options): def handle(self, *args, **options):

View File

@ -16,5 +16,6 @@
from rest_framework.renderers import UnicodeJSONRenderer from rest_framework.renderers import UnicodeJSONRenderer
class ExportRenderer(UnicodeJSONRenderer): class ExportRenderer(UnicodeJSONRenderer):
pass pass

View File

@ -53,7 +53,6 @@ def dump_project(self, user, project):
email.send() email.send()
return return
deletion_date = timezone.now() + datetime.timedelta(seconds=settings.EXPORTS_TTL) deletion_date = timezone.now() + datetime.timedelta(seconds=settings.EXPORTS_TTL)
ctx = { ctx = {
"url": url, "url": url,

View File

@ -20,5 +20,6 @@ from taiga.base import throttling
class ImportModeRateThrottle(throttling.UserRateThrottle): class ImportModeRateThrottle(throttling.UserRateThrottle):
scope = "import-mode" scope = "import-mode"
class ImportDumpModeRateThrottle(throttling.UserRateThrottle): class ImportDumpModeRateThrottle(throttling.UserRateThrottle):
scope = "import-dump-mode" scope = "import-dump-mode"

View File

@ -61,7 +61,7 @@ class BitBucketViewSet(BaseWebhookApiViewSet):
valid_origin_ips = bitbucket_config.get("valid_origin_ips", valid_origin_ips = bitbucket_config.get("valid_origin_ips",
settings.BITBUCKET_VALID_ORIGIN_IPS) settings.BITBUCKET_VALID_ORIGIN_IPS)
origin_ip = get_real_ip(request) origin_ip = get_real_ip(request)
if valid_origin_ips and (not origin_ip or not origin_ip in valid_origin_ips): if valid_origin_ips and (not origin_ip or origin_ip not in valid_origin_ips):
return False return False
return project_secret == secret_key return project_secret == secret_key

View File

@ -15,12 +15,11 @@
# 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 re import re
import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from taiga.base import exceptions as exc from taiga.base import exceptions as exc
from taiga.projects.models import Project, IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
from taiga.projects.issues.models import Issue from taiga.projects.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task
from taiga.projects.userstories.models import UserStory from taiga.projects.userstories.models import UserStory
@ -33,6 +32,7 @@ from .services import get_bitbucket_user
import json import json
class PushEventHook(BaseEventHook): class PushEventHook(BaseEventHook):
def process_event(self): def process_event(self):
if self.payload is None: if self.payload is None:

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
from django.core.files import File from django.core.files import File
import uuid import uuid
def create_github_system_user(apps, schema_editor): def create_github_system_user(apps, schema_editor):
# We get the model from the versioned app registry; # We get the model from the versioned app registry;
# if we directly import it, it'll be the wrong version # if we directly import it, it'll be the wrong version

View File

@ -35,7 +35,7 @@ def get_or_generate_config(project):
url = reverse("bitbucket-hook-list") url = reverse("bitbucket-hook-list")
url = get_absolute_url(url) url = get_absolute_url(url)
url = "%s?project=%s&key=%s"%(url, project.id, g_config["secret"]) url = "%s?project=%s&key=%s" % (url, project.id, g_config["secret"])
g_config["webhooks_url"] = url g_config["webhooks_url"] = url
return g_config return g_config

View File

@ -46,7 +46,7 @@ class GitHubViewSet(BaseWebhookApiViewSet):
secret = project.modules_config.config.get("github", {}).get("secret", "") secret = project.modules_config.config.get("github", {}).get("secret", "")
secret = bytes(secret.encode("utf-8")) secret = bytes(secret.encode("utf-8"))
mac = hmac.new(secret, msg=request.body,digestmod=hashlib.sha1) mac = hmac.new(secret, msg=request.body, digestmod=hashlib.sha1)
return hmac.compare_digest(mac.hexdigest(), signature) return hmac.compare_digest(mac.hexdigest(), signature)
def _get_event_name(self, request): def _get_event_name(self, request):

View File

@ -16,7 +16,7 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from taiga.projects.models import Project, IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
from taiga.projects.issues.models import Issue from taiga.projects.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
from django.core.files import File from django.core.files import File
import uuid import uuid
def create_github_system_user(apps, schema_editor): def create_github_system_user(apps, schema_editor):
# We get the model from the versioned app registry; # We get the model from the versioned app registry;
# if we directly import it, it'll be the wrong version # if we directly import it, it'll be the wrong version

View File

@ -27,11 +27,11 @@ def get_or_generate_config(project):
if config and "github" in config: if config and "github" in config:
g_config = project.modules_config.config["github"] g_config = project.modules_config.config["github"]
else: else:
g_config = {"secret": uuid.uuid4().hex } g_config = {"secret": uuid.uuid4().hex}
url = reverse("github-hook-list") url = reverse("github-hook-list")
url = get_absolute_url(url) url = get_absolute_url(url)
url = "%s?project=%s"%(url, project.id) url = "%s?project=%s" % (url, project.id)
g_config["webhooks_url"] = url g_config["webhooks_url"] = url
return g_config return g_config

View File

@ -14,7 +14,6 @@
# 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.conf import settings from django.conf import settings
from ipware.ip import get_real_ip from ipware.ip import get_real_ip

View File

@ -19,7 +19,7 @@ import os
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from taiga.projects.models import Project, IssueStatus, TaskStatus, UserStoryStatus from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus
from taiga.projects.issues.models import Issue from taiga.projects.issues.models import Issue
from taiga.projects.tasks.models import Task from taiga.projects.tasks.models import Task

View File

@ -1,11 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
from django.core.files import File from django.core.files import File
import uuid import uuid
def create_github_system_user(apps, schema_editor): def create_github_system_user(apps, schema_editor):
# We get the model from the versioned app registry; # We get the model from the versioned app registry;
# if we directly import it, it'll be the wrong version # if we directly import it, it'll be the wrong version

View File

@ -26,6 +26,7 @@ from taiga.base.utils.slug import slugify
import re import re
class WikiLinkExtension(Extension): class WikiLinkExtension(Extension):
def __init__(self, project, *args, **kwargs): def __init__(self, project, *args, **kwargs):
self.project = project self.project = project
@ -66,6 +67,7 @@ class WikiLinksPattern(Pattern):
SLUG_RE = re.compile(r"^[-a-zA-Z0-9_]+$") SLUG_RE = re.compile(r"^[-a-zA-Z0-9_]+$")
class RelativeLinksTreeprocessor(Treeprocessor): class RelativeLinksTreeprocessor(Treeprocessor):
def __init__(self, md, project): def __init__(self, md, project):
self.project = project self.project = project

View File

@ -22,6 +22,7 @@ import bleach
import html5lib import html5lib
from html5lib.serializer.htmlserializer import HTMLSerializer from html5lib.serializer.htmlserializer import HTMLSerializer
def _serialize(domtree): def _serialize(domtree):
walker = html5lib.treewalkers.getTreeWalker('etree') walker = html5lib.treewalkers.getTreeWalker('etree')
stream = walker(domtree) stream = walker(domtree)
@ -32,7 +33,7 @@ def _serialize(domtree):
return serializer.render(stream) return serializer.render(stream)
bleach._serialize = _serialize bleach._serialize = _serialize
### END PATCH # END PATCH
from django.core.cache import cache from django.core.cache import cache
from django.utils.encoding import force_bytes from django.utils.encoding import force_bytes

View File

@ -23,7 +23,6 @@ from taiga.base.api import GenericViewSet
from . import serializers from . import serializers
from . import service from . import service
from . import permissions from . import permissions
from . import models
class TimelineViewSet(GenericViewSet): class TimelineViewSet(GenericViewSet):

View File

@ -36,5 +36,3 @@ class TimelineAppConfig(AppConfig):
sender=apps.get_model("projects", "Membership")) sender=apps.get_model("projects", "Membership"))
signals.post_delete.connect(handlers.delete_membership_push_to_timeline, signals.post_delete.connect(handlers.delete_membership_push_to_timeline,
sender=apps.get_model("projects", "Membership")) sender=apps.get_model("projects", "Membership"))

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 taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm, from taiga.base.api.permissions import (TaigaResourcePermission, HasProjectPerm,
IsProjectOwner, AllowAny) AllowAny)
class UserTimelinePermission(TaigaResourcePermission): class UserTimelinePermission(TaigaResourcePermission):

View File

@ -17,8 +17,6 @@
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db import IntegrityError from django.db import IntegrityError
from rest_framework.permissions import IsAuthenticated
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet
from taiga.base import exceptions as exc from taiga.base import exceptions as exc

View File

@ -16,6 +16,7 @@
from taiga.base import filters from taiga.base import filters
class StorageEntriesFilterBackend(filters.FilterBackend): class StorageEntriesFilterBackend(filters.FilterBackend):
def filter_queryset(self, request, queryset, view): def filter_queryset(self, request, queryset, view):
queryset = super().filter_queryset(request, queryset, view) queryset = super().filter_queryset(request, queryset, view)

View File

@ -22,7 +22,7 @@ from django_pgjson.fields import JsonField
class StorageEntry(models.Model): class StorageEntry(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False, owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False,
related_name="storage_entries", verbose_name=_("owner")) related_name="storage_entries", verbose_name=_("owner"))
created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False, created_date = models.DateTimeField(auto_now_add=True, null=False, blank=False,
verbose_name=_("created date")) verbose_name=_("created date"))
modified_date = models.DateTimeField(auto_now=True, null=False, blank=False, modified_date = models.DateTimeField(auto_now=True, null=False, blank=False,

View File

@ -14,14 +14,11 @@
# 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 taiga.base import filters from taiga.base import filters
from taiga.base import response from taiga.base import response
from taiga.base.api import ModelCrudViewSet from taiga.base.api import ModelCrudViewSet
from taiga.base.api import ModelListViewSet from taiga.base.api import ModelListViewSet
from taiga.base.api.utils import get_object_or_404
from taiga.base.decorators import detail_route from taiga.base.decorators import detail_route
from . import models from . import models

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):

View File

@ -24,7 +24,7 @@ class Webhook(models.Model):
project = models.ForeignKey("projects.Project", null=False, blank=False, project = models.ForeignKey("projects.Project", null=False, blank=False,
related_name="webhooks") related_name="webhooks")
name = models.CharField(max_length=250, null=False, blank=False, name = models.CharField(max_length=250, null=False, blank=False,
verbose_name=_("name")) verbose_name=_("name"))
url = models.URLField(null=False, blank=False, verbose_name=_("URL")) url = models.URLField(null=False, blank=False, verbose_name=_("URL"))
key = models.TextField(null=False, blank=False, verbose_name=_("secret key")) key = models.TextField(null=False, blank=False, verbose_name=_("secret key"))

View File

@ -35,6 +35,7 @@ class HistoryDiffField(serializers.Field):
class WebhookSerializer(serializers.ModelSerializer): class WebhookSerializer(serializers.ModelSerializer):
logs_counter = serializers.SerializerMethodField("get_logs_counter") logs_counter = serializers.SerializerMethodField("get_logs_counter")
class Meta: class Meta:
model = Webhook model = Webhook
@ -61,6 +62,7 @@ class UserSerializer(serializers.Serializer):
def get_name(self, obj): def get_name(self, obj):
return obj.full_name return obj.full_name
class PointSerializer(serializers.Serializer): class PointSerializer(serializers.Serializer):
id = serializers.SerializerMethodField("get_pk") id = serializers.SerializerMethodField("get_pk")
name = serializers.SerializerMethodField("get_name") name = serializers.SerializerMethodField("get_name")

View File

@ -86,7 +86,7 @@ def _send_request(webhook_id, url, key, data):
duration=0) duration=0)
session.close() session.close()
ids = [webhook_log.id for webhook_log in WebhookLog.objects.filter(webhook_id=webhook_id).order_by("-id")[10:]] ids = [log.id for log in WebhookLog.objects.filter(webhook_id=webhook_id).order_by("-id")[10:]]
WebhookLog.objects.filter(id__in=ids).delete() WebhookLog.objects.filter(id__in=ids).delete()
return webhook_log return webhook_log
@ -135,4 +135,3 @@ def test_webhook(webhook_id, url, key):
data['type'] = "test" data['type'] = "test"
return _send_request(webhook_id, url, key, data) return _send_request(webhook_id, url, key, data)