mirror of
https://github.com/taigaio/taiga-back
synced 2025-10-05 15:52:48 +02:00
chore: migrate to django 3.2
This commit is contained in:
committed by
David Barragán Merino
parent
04becaf240
commit
e7fad0b5cc
2
.github/workflows/tests-and-coverall.yml
vendored
2
.github/workflows/tests-and-coverall.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
|
||||
python-version: [ '3.8', '3.9', '3.10', '3.11' ]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
|
@@ -2,11 +2,15 @@
|
||||
|
||||
## 6.6.0 (unreleased)
|
||||
|
||||
- Adapt Dockerfile to the removal of psycopg2-binary
|
||||
- DOCKER: Added env `POSTGRES_SSLMODE` with default value disabled (thanks to [@ribeiromiranda](https://github.com/ribeiromiranda))
|
||||
- Add support for Python 3.11
|
||||
- Remove support for Python 3.7
|
||||
- Upgrade to Django 3.2 (and upgrade some other dependencies)
|
||||
- Fix some silent css error in email templates (thanks to [@sarbanha](https://github.com/sarbanha))
|
||||
- Improves the performance of operations on tags (thanks to [@theriverman](https://github.com/theriverman))
|
||||
- Remove `write` permission from Trello importer (thanks to [@northben](https://github.com/northben))
|
||||
- DOCKER: Use python-3.11-slim as base image
|
||||
- DOCKER: Adapt Dockerfile to the removal of psycopg2-binary
|
||||
- DOCKER: Added env `POSTGRES_SSLMODE` with default value disabled (thanks to [@ribeiromiranda](https://github.com/ribeiromiranda))
|
||||
|
||||
## 6.5.2 (2022-09-26)
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
FROM python:3.7-slim
|
||||
FROM python:3.11-slim
|
||||
LABEL maintainer="support@taiga.io"
|
||||
|
||||
# Avoid prompting for configuration
|
||||
|
@@ -1,4 +1,10 @@
|
||||
[pytest]
|
||||
DJANGO_SETTINGS_MODULE = tests.config
|
||||
filterwarnings =
|
||||
default::DeprecationWarning
|
||||
# Django
|
||||
ignore::django.utils.deprecation.RemovedInDjango40Warning
|
||||
ignore::django.utils.deprecation.RemovedInDjango41Warning
|
||||
ignore:'cgi' is deprecated and slated for removal in Python 3.13:DeprecationWarning
|
||||
ignore:Use setlocale\(\), getencoding\(\) and getlocale\(\) instead:DeprecationWarning
|
||||
# PyJWT
|
||||
ignore::jwt.warnings.RemovedInPyjwt3Warning
|
||||
|
@@ -1,22 +1,22 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.7
|
||||
# To update, run:
|
||||
# This file is autogenerated by pip-compile with Python 3.8
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile requirements-devel.in
|
||||
#
|
||||
attrs==21.4.0
|
||||
attrs==22.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pytest
|
||||
certifi==2021.10.8
|
||||
certifi==2022.12.7
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
charset-normalizer==2.0.10
|
||||
charset-normalizer==2.0.12
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
coverage==6.2
|
||||
coverage==6.5.0
|
||||
# via
|
||||
# -r requirements-devel.in
|
||||
# coveralls
|
||||
@@ -26,40 +26,31 @@ docopt==0.6.2
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# coveralls
|
||||
ecdsa==0.17.0
|
||||
ecdsa==0.18.0
|
||||
# via python-jose
|
||||
exceptiongroup==1.1.0
|
||||
# via pytest
|
||||
factory-boy==3.2.1
|
||||
# via -r requirements-devel.in
|
||||
faker==11.3.0
|
||||
faker==17.0.0
|
||||
# via factory-boy
|
||||
idna==3.3
|
||||
idna==3.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
importlib-metadata==4.10.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pluggy
|
||||
# pytest
|
||||
iniconfig==1.1.1
|
||||
iniconfig==2.0.0
|
||||
# via pytest
|
||||
packaging==21.3
|
||||
packaging==23.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pytest
|
||||
pluggy==1.0.0
|
||||
# via pytest
|
||||
py==1.11.0
|
||||
# via pytest
|
||||
pyasn1==0.4.8
|
||||
# via
|
||||
# python-jose
|
||||
# rsa
|
||||
pyparsing==3.0.6
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# packaging
|
||||
pytest==6.2.5
|
||||
pytest==7.2.1
|
||||
# via
|
||||
# -r requirements-devel.in
|
||||
# pytest-django
|
||||
@@ -75,27 +66,16 @@ requests==2.27.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# coveralls
|
||||
rsa==4.8
|
||||
rsa==4.9
|
||||
# via python-jose
|
||||
six==1.16.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# ecdsa
|
||||
# python-dateutil
|
||||
text-unidecode==1.3
|
||||
# via faker
|
||||
toml==0.10.2
|
||||
tomli==2.0.1
|
||||
# via pytest
|
||||
typing-extensions==4.0.1
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# faker
|
||||
# importlib-metadata
|
||||
urllib3==1.26.8
|
||||
urllib3==1.26.14
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# requests
|
||||
zipp==1.2.0
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# importlib-metadata
|
||||
|
@@ -1,2 +1,2 @@
|
||||
factory_boy==2.11.1
|
||||
pytest==4.4.1
|
||||
factory-boy==3.2.1
|
||||
pytest==7.2.1
|
||||
|
@@ -1,35 +1,35 @@
|
||||
asana
|
||||
bleach
|
||||
celery==5.2.3
|
||||
bleach<5
|
||||
celery==5.2.7
|
||||
diff-match-patch==20121119
|
||||
django-ipware==1.1.6
|
||||
django-jinja==2.7.0
|
||||
django-picklefield==0.3.2
|
||||
django-pglocks==1.0.2
|
||||
django-jinja
|
||||
django-picklefield==3.1
|
||||
django-pglocks==1.0.4
|
||||
django-sampledatahelper==0.4.1
|
||||
django-sites==0.10
|
||||
django-sites==0.11
|
||||
django-sr==0.0.4
|
||||
djmail==1.0.1
|
||||
easy-thumbnails==2.7.0
|
||||
djmail==2.0.0
|
||||
easy-thumbnails==2.8.5
|
||||
gunicorn==20.1.0
|
||||
netaddr==0.7.19
|
||||
netaddr<0.9
|
||||
premailer==3.0.1
|
||||
psd-tools==1.9.18
|
||||
psycopg2<2.9 # required by django
|
||||
pymdown-extensions==8.1.1
|
||||
psycopg2<2.10 # required by django
|
||||
python-dateutil==2.7.5
|
||||
python-magic==0.4.15
|
||||
pytz
|
||||
raven
|
||||
redis==2.10.5
|
||||
redis==4.5.1
|
||||
requests==2.27.1
|
||||
requests_oauthlib
|
||||
rudder-sdk-python==1.0.0b1
|
||||
serpy==0.1.1
|
||||
webcolors==1.9.1
|
||||
CairoSVG>=2.5.1
|
||||
Django>=2.2,<3
|
||||
Markdown==3.3.4
|
||||
Django>=3,<4
|
||||
Markdown==3.4.1
|
||||
pymdown-extensions==9.9.2
|
||||
Pillow
|
||||
Unidecode==0.4.20
|
||||
Pygments>=2.7.4
|
||||
|
127
requirements.txt
127
requirements.txt
@@ -1,16 +1,20 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.7
|
||||
# To update, run:
|
||||
# This file is autogenerated by pip-compile with Python 3.8
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
aggdraw==1.3.12
|
||||
aggdraw==1.3.15
|
||||
# via psd-tools
|
||||
amqp==5.0.9
|
||||
amqp==5.1.1
|
||||
# via kombu
|
||||
asana==0.10.3
|
||||
asana==3.1.0
|
||||
# via -r requirements.in
|
||||
attrs==21.4.0
|
||||
asgiref==3.6.0
|
||||
# via django
|
||||
async-timeout==4.0.2
|
||||
# via redis
|
||||
attrs==22.2.0
|
||||
# via psd-tools
|
||||
backoff==1.6.0
|
||||
# via rudder-sdk-python
|
||||
@@ -18,23 +22,21 @@ billiard==3.6.4.0
|
||||
# via celery
|
||||
bleach==4.1.0
|
||||
# via -r requirements.in
|
||||
cached-property==1.5.2
|
||||
# via kombu
|
||||
cairocffi==1.3.0
|
||||
cairocffi==1.4.0
|
||||
# via cairosvg
|
||||
cairosvg==2.5.2
|
||||
cairosvg==2.6.0
|
||||
# via -r requirements.in
|
||||
celery==5.2.3
|
||||
celery==5.2.7
|
||||
# via -r requirements.in
|
||||
certifi==2021.10.8
|
||||
certifi==2022.12.7
|
||||
# via requests
|
||||
cffi==1.15.0
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# cairocffi
|
||||
# cryptography
|
||||
charset-normalizer==2.0.10
|
||||
charset-normalizer==2.0.12
|
||||
# via requests
|
||||
click==8.0.3
|
||||
click==8.1.3
|
||||
# via
|
||||
# celery
|
||||
# click-didyoumean
|
||||
@@ -46,79 +48,76 @@ click-plugins==1.1.1
|
||||
# via celery
|
||||
click-repl==0.2.0
|
||||
# via celery
|
||||
cryptography==3.4.8
|
||||
cryptography==39.0.1
|
||||
# via oauthlib
|
||||
cssselect==1.1.0
|
||||
cssselect==1.2.0
|
||||
# via premailer
|
||||
cssselect2==0.4.1
|
||||
cssselect2==0.7.0
|
||||
# via cairosvg
|
||||
cssutils==2.3.0
|
||||
cssutils==2.6.0
|
||||
# via premailer
|
||||
defusedxml==0.7.1
|
||||
# via cairosvg
|
||||
diff-match-patch==20121119
|
||||
# via -r requirements.in
|
||||
django==2.2.26
|
||||
django==3.2.18
|
||||
# via
|
||||
# -r requirements.in
|
||||
# django-jinja
|
||||
# django-picklefield
|
||||
# django-sampledatahelper
|
||||
# django-sites
|
||||
# django-sr
|
||||
# easy-thumbnails
|
||||
django-ipware==1.1.6
|
||||
# via -r requirements.in
|
||||
django-jinja==2.7.0
|
||||
django-jinja==2.10.2
|
||||
# via -r requirements.in
|
||||
django-pglocks==1.0.2
|
||||
django-pglocks==1.0.4
|
||||
# via -r requirements.in
|
||||
django-picklefield==0.3.2
|
||||
django-picklefield==3.1
|
||||
# via -r requirements.in
|
||||
django-sampledatahelper==0.4.1
|
||||
# via -r requirements.in
|
||||
django-sites==0.10
|
||||
django-sites==0.11
|
||||
# via -r requirements.in
|
||||
django-sr==0.0.4
|
||||
# via -r requirements.in
|
||||
djmail==1.0.1
|
||||
djmail==2.0.0
|
||||
# via -r requirements.in
|
||||
docopt==0.6.2
|
||||
# via psd-tools
|
||||
easy-thumbnails==2.7.0
|
||||
easy-thumbnails==2.8.5
|
||||
# via -r requirements.in
|
||||
gunicorn==20.1.0
|
||||
# via -r requirements.in
|
||||
html5lib==1.1
|
||||
# via -r requirements.in
|
||||
idna==3.3
|
||||
idna==3.4
|
||||
# via requests
|
||||
imageio==2.13.5
|
||||
imageio==2.25.1
|
||||
# via scikit-image
|
||||
importlib-metadata==4.10.0
|
||||
# via
|
||||
# click
|
||||
# cssutils
|
||||
# kombu
|
||||
# markdown
|
||||
jinja2==3.0.3
|
||||
importlib-metadata==6.0.0
|
||||
# via markdown
|
||||
jinja2==3.1.2
|
||||
# via django-jinja
|
||||
kombu==5.2.3
|
||||
kombu==5.2.4
|
||||
# via celery
|
||||
lxml==4.7.1
|
||||
lxml==4.9.2
|
||||
# via premailer
|
||||
markdown==3.3.4
|
||||
markdown==3.4.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pymdown-extensions
|
||||
markupsafe==2.0.1
|
||||
markupsafe==2.1.2
|
||||
# via jinja2
|
||||
monotonic==1.6
|
||||
# via rudder-sdk-python
|
||||
netaddr==0.7.19
|
||||
netaddr==0.8.0
|
||||
# via -r requirements.in
|
||||
networkx==2.6.3
|
||||
networkx==3.0
|
||||
# via scikit-image
|
||||
numpy==1.21.5
|
||||
numpy==1.24.2
|
||||
# via
|
||||
# imageio
|
||||
# psd-tools
|
||||
@@ -126,15 +125,15 @@ numpy==1.21.5
|
||||
# scikit-image
|
||||
# scipy
|
||||
# tifffile
|
||||
oauthlib[signedtoken]==3.1.1
|
||||
oauthlib[signedtoken]==3.2.2
|
||||
# via
|
||||
# -r requirements.in
|
||||
# requests-oauthlib
|
||||
packaging==21.3
|
||||
packaging==23.0
|
||||
# via
|
||||
# bleach
|
||||
# scikit-image
|
||||
pillow==9.0.0
|
||||
pillow==9.4.0
|
||||
# via
|
||||
# -r requirements.in
|
||||
# cairosvg
|
||||
@@ -144,39 +143,37 @@ pillow==9.0.0
|
||||
# scikit-image
|
||||
premailer==3.0.1
|
||||
# via -r requirements.in
|
||||
prompt-toolkit==3.0.24
|
||||
prompt-toolkit==3.0.36
|
||||
# via click-repl
|
||||
psd-tools==1.9.18
|
||||
# via -r requirements.in
|
||||
psycopg2==2.8.6
|
||||
psycopg2==2.9.5
|
||||
# via -r requirements.in
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pygments==2.11.2
|
||||
pygments==2.14.0
|
||||
# via -r requirements.in
|
||||
pyjwt==2.3.0
|
||||
pyjwt==2.6.0
|
||||
# via oauthlib
|
||||
pymdown-extensions==8.1.1
|
||||
pymdown-extensions==9.9.2
|
||||
# via -r requirements.in
|
||||
pyparsing==3.0.6
|
||||
# via packaging
|
||||
python-dateutil==2.7.5
|
||||
# via
|
||||
# -r requirements.in
|
||||
# rudder-sdk-python
|
||||
python-magic==0.4.15
|
||||
# via -r requirements.in
|
||||
pytz==2021.3
|
||||
pytz==2022.7.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# celery
|
||||
# django
|
||||
# sampledata
|
||||
pywavelets==1.2.0
|
||||
pywavelets==1.4.1
|
||||
# via scikit-image
|
||||
raven==6.10.0
|
||||
# via -r requirements.in
|
||||
redis==2.10.5
|
||||
redis==4.5.1
|
||||
# via -r requirements.in
|
||||
requests==2.27.1
|
||||
# via
|
||||
@@ -185,7 +182,7 @@ requests==2.27.1
|
||||
# premailer
|
||||
# requests-oauthlib
|
||||
# rudder-sdk-python
|
||||
requests-oauthlib==1.3.0
|
||||
requests-oauthlib==1.3.1
|
||||
# via
|
||||
# -r requirements.in
|
||||
# asana
|
||||
@@ -193,9 +190,9 @@ rudder-sdk-python==1.0.0b1
|
||||
# via -r requirements.in
|
||||
sampledata==0.3.7
|
||||
# via django-sampledatahelper
|
||||
scikit-image==0.19.1
|
||||
scikit-image==0.19.3
|
||||
# via psd-tools
|
||||
scipy==1.7.3
|
||||
scipy==1.10.0
|
||||
# via
|
||||
# psd-tools
|
||||
# scikit-image
|
||||
@@ -203,9 +200,9 @@ serpy==0.1.1
|
||||
# via -r requirements.in
|
||||
six==1.16.0
|
||||
# via
|
||||
# asana
|
||||
# bleach
|
||||
# click-repl
|
||||
# django-pglocks
|
||||
# django-sampledatahelper
|
||||
# html5lib
|
||||
# python-dateutil
|
||||
@@ -213,26 +210,24 @@ six==1.16.0
|
||||
# sampledata
|
||||
# serpy
|
||||
# webcolors
|
||||
sqlparse==0.4.2
|
||||
sqlparse==0.4.3
|
||||
# via django
|
||||
tifffile==2021.11.2
|
||||
tifffile==2023.2.3
|
||||
# via scikit-image
|
||||
tinycss2==1.1.1
|
||||
tinycss2==1.2.1
|
||||
# via
|
||||
# cairosvg
|
||||
# cssselect2
|
||||
typing-extensions==4.0.1
|
||||
# via importlib-metadata
|
||||
unidecode==0.4.20
|
||||
# via -r requirements.in
|
||||
urllib3==1.26.8
|
||||
urllib3==1.26.14
|
||||
# via requests
|
||||
vine==5.0.0
|
||||
# via
|
||||
# amqp
|
||||
# celery
|
||||
# kombu
|
||||
wcwidth==0.2.5
|
||||
wcwidth==0.2.6
|
||||
# via prompt-toolkit
|
||||
webcolors==1.9.1
|
||||
# via -r requirements.in
|
||||
|
@@ -32,6 +32,9 @@ DATABASES = {
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||
|
||||
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
from functools import partial
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base import exceptions as exc
|
||||
|
@@ -33,7 +33,7 @@ import bleach
|
||||
import re
|
||||
|
||||
from django.core import validators as core_validators
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.api import serializers
|
||||
from taiga.base.exceptions import ValidationError
|
||||
|
@@ -8,4 +8,4 @@
|
||||
import django.dispatch
|
||||
|
||||
|
||||
user_registered = django.dispatch.Signal(providing_args=["user"])
|
||||
user_registered = django.dispatch.Signal() # providing_args=["user"]
|
||||
|
@@ -29,4 +29,3 @@
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
default_app_config = 'taiga.auth.token_denylist.apps.TokenDenylistConfig'
|
||||
|
@@ -35,6 +35,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
from .models import DenylistedToken, OutstandingToken
|
||||
|
||||
|
||||
@admin.register(OutstandingToken)
|
||||
class OutstandingTokenAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'jti',
|
||||
@@ -74,9 +75,9 @@ class OutstandingTokenAdmin(admin.ModelAdmin):
|
||||
)
|
||||
|
||||
|
||||
admin.site.register(OutstandingToken, OutstandingTokenAdmin)
|
||||
|
||||
|
||||
@admin.register(DenylistedToken)
|
||||
class DenylistedTokenAdmin(admin.ModelAdmin):
|
||||
list_display = (
|
||||
'token_jti',
|
||||
@@ -98,25 +99,32 @@ class DenylistedTokenAdmin(admin.ModelAdmin):
|
||||
|
||||
return qs.select_related('token__user')
|
||||
|
||||
@admin.display(
|
||||
description=_('jti'),
|
||||
ordering='token__jti',
|
||||
)
|
||||
def token_jti(self, obj):
|
||||
return obj.token.jti
|
||||
token_jti.short_description = _('jti')
|
||||
token_jti.admin_order_field = 'token__jti'
|
||||
|
||||
@admin.display(
|
||||
description=_('user'),
|
||||
ordering='token__user',
|
||||
)
|
||||
def token_user(self, obj):
|
||||
return obj.token.user
|
||||
token_user.short_description = _('user')
|
||||
token_user.admin_order_field = 'token__user'
|
||||
|
||||
@admin.display(
|
||||
description=_('created at'),
|
||||
ordering='token__created_at',
|
||||
)
|
||||
def token_created_at(self, obj):
|
||||
return obj.token.created_at
|
||||
token_created_at.short_description = _('created at')
|
||||
token_created_at.admin_order_field = 'token__created_at'
|
||||
|
||||
@admin.display(
|
||||
description=_('expires at'),
|
||||
ordering='token__expires_at',
|
||||
)
|
||||
def token_expires_at(self, obj):
|
||||
return obj.token.expires_at
|
||||
token_expires_at.short_description = _('expires at')
|
||||
token_expires_at.admin_order_field = 'token__expires_at'
|
||||
|
||||
|
||||
admin.site.register(DenylistedToken, DenylistedTokenAdmin)
|
||||
|
@@ -5,4 +5,3 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.base.apps.BaseAppConfig"
|
||||
|
@@ -50,7 +50,7 @@ def get_authorization_header(request):
|
||||
|
||||
Hide some test client ickyness where the header can be unicode.
|
||||
"""
|
||||
auth = request.META.get('HTTP_AUTHORIZATION', b'')
|
||||
auth = request.headers.get('authorization', b'')
|
||||
if type(auth) == type(''):
|
||||
# Work around django test client oddness
|
||||
auth = auth.encode(HTTP_HEADER_ENCODING)
|
||||
|
@@ -42,17 +42,17 @@ from django.core import validators
|
||||
from django.db.models.fields import BLANK_CHOICE_DASH
|
||||
from django.forms import widgets
|
||||
from django.http import QueryDict
|
||||
from django.utils import six
|
||||
import six
|
||||
from django.utils import timezone
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from django.utils.dateparse import parse_time
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.encoding import is_protected_type
|
||||
from django.utils.functional import Promise
|
||||
from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from taiga.base.exceptions import ValidationError
|
||||
|
||||
@@ -168,7 +168,7 @@ class Field(object):
|
||||
self.source = source
|
||||
|
||||
if label is not None:
|
||||
self.label = smart_text(label)
|
||||
self.label = smart_str(label)
|
||||
else:
|
||||
self.label = None
|
||||
|
||||
@@ -250,7 +250,7 @@ class Field(object):
|
||||
for key, val in value.items():
|
||||
ret[key] = self.to_native(val)
|
||||
return ret
|
||||
return force_text(value)
|
||||
return force_str(value)
|
||||
|
||||
def attributes(self):
|
||||
"""
|
||||
@@ -269,7 +269,7 @@ class Field(object):
|
||||
for attr in optional_attrs:
|
||||
value = getattr(self, attr, None)
|
||||
if value is not None and value != "":
|
||||
metadata[attr] = force_text(value, strings_only=True)
|
||||
metadata[attr] = force_str(value, strings_only=True)
|
||||
return metadata
|
||||
|
||||
|
||||
@@ -509,12 +509,12 @@ class CharField(WritableField):
|
||||
if value in validators.EMPTY_VALUES:
|
||||
return ""
|
||||
|
||||
return smart_text(value)
|
||||
return smart_str(value)
|
||||
|
||||
def to_native(self, value):
|
||||
ret = super(CharField, self).to_native(value)
|
||||
if self.i18n:
|
||||
ret = ugettext(ret)
|
||||
ret = gettext(ret)
|
||||
|
||||
return ret
|
||||
|
||||
@@ -593,10 +593,10 @@ class ChoiceField(WritableField):
|
||||
if isinstance(v, (list, tuple)):
|
||||
# This is an optgroup, so look inside the group for options
|
||||
for k2, v2 in v:
|
||||
if value == smart_text(k2):
|
||||
if value == smart_str(k2):
|
||||
return True
|
||||
else:
|
||||
if value == smart_text(k) or value == k:
|
||||
if value == smart_str(k) or value == k:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -946,7 +946,7 @@ class DecimalField(WritableField):
|
||||
"""
|
||||
if value in validators.EMPTY_VALUES:
|
||||
return None
|
||||
value = smart_text(value).strip()
|
||||
value = smart_str(value).strip()
|
||||
try:
|
||||
value = Decimal(value)
|
||||
except DecimalException:
|
||||
|
@@ -35,7 +35,7 @@ import warnings
|
||||
|
||||
from django.http import Http404
|
||||
from django.db import transaction as tx
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base import response
|
||||
from taiga.base.exceptions import ValidationError
|
||||
|
@@ -120,6 +120,6 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
||||
|
||||
Allows URL style accept override. eg. "?accept=application/json"
|
||||
"""
|
||||
header = request.META.get("HTTP_ACCEPT", "*/*")
|
||||
header = request.headers.get("accept", "*/*")
|
||||
header = request.QUERY_PARAMS.get(self.settings.URL_ACCEPT_OVERRIDE, header)
|
||||
return [token.strip() for token in header.split(",")]
|
||||
|
@@ -14,7 +14,7 @@ from django.core.paginator import (
|
||||
)
|
||||
from django.http import Http404
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .settings import api_settings
|
||||
|
||||
@@ -148,7 +148,7 @@ class PaginationMixin(object):
|
||||
|
||||
Otherwise defaults to using `self.paginate_by`.
|
||||
"""
|
||||
if "HTTP_X_DISABLE_PAGINATION" in self.request.META:
|
||||
if "x-disable-pagination" in self.request.headers:
|
||||
return None
|
||||
|
||||
if queryset is not None:
|
||||
@@ -172,10 +172,10 @@ class PaginationMixin(object):
|
||||
Paginate a queryset if required, either returning a page object,
|
||||
or `None` if pagination is not configured for this view.
|
||||
"""
|
||||
if "HTTP_X_DISABLE_PAGINATION" in self.request.META:
|
||||
if "x-disable-pagination" in self.request.headers:
|
||||
return None
|
||||
|
||||
if "HTTP_X_LAZY_PAGINATION" in self.request.META:
|
||||
if "x-lazy-pagination" in self.request.headers:
|
||||
self.paginator_class = LazyPaginator
|
||||
|
||||
deprecated_style = False
|
||||
@@ -226,7 +226,7 @@ class PaginationMixin(object):
|
||||
if page is None:
|
||||
return page
|
||||
|
||||
if not "HTTP_X_LAZY_PAGINATION" in self.request.META:
|
||||
if not "x-lazy-pagination" in self.request.headers:
|
||||
self.headers["x-pagination-count"] = page.paginator.count
|
||||
|
||||
self.headers["x-paginated"] = "true"
|
||||
|
@@ -43,7 +43,7 @@ from django.http import QueryDict
|
||||
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
|
||||
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
|
||||
|
||||
from django.utils import six
|
||||
import six
|
||||
|
||||
from taiga.base.exceptions import ParseError
|
||||
from taiga.base.api import renderers
|
||||
|
@@ -12,7 +12,7 @@ from functools import reduce
|
||||
|
||||
from taiga.permissions.services import user_has_perm, is_project_admin
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
######################################################################
|
||||
|
@@ -43,8 +43,8 @@ from django import forms
|
||||
from django.db.models.fields import BLANK_CHOICE_DASH
|
||||
from django.forms import widgets
|
||||
from django.forms.models import ModelChoiceIterator
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .fields import Field, WritableField, get_component, is_simple_callable
|
||||
from .reverse import reverse
|
||||
@@ -118,8 +118,8 @@ class RelatedField(WritableField):
|
||||
"""
|
||||
Return a readable representation for use with eg. select widgets.
|
||||
"""
|
||||
desc = smart_text(obj)
|
||||
ident = smart_text(self.to_native(obj))
|
||||
desc = smart_str(obj)
|
||||
ident = smart_str(self.to_native(obj))
|
||||
if desc == ident:
|
||||
return desc
|
||||
return "%s - %s" % (desc, ident)
|
||||
@@ -245,8 +245,8 @@ class PrimaryKeyRelatedField(RelatedField):
|
||||
"""
|
||||
Return a readable representation for use with eg. select widgets.
|
||||
"""
|
||||
desc = smart_text(obj)
|
||||
ident = smart_text(self.to_native(obj.pk))
|
||||
desc = smart_str(obj)
|
||||
ident = smart_str(self.to_native(obj.pk))
|
||||
if desc == ident:
|
||||
return desc
|
||||
return "%s - %s" % (desc, ident)
|
||||
@@ -262,7 +262,7 @@ class PrimaryKeyRelatedField(RelatedField):
|
||||
try:
|
||||
return self.queryset.get(pk=data)
|
||||
except ObjectDoesNotExist:
|
||||
msg = self.error_messages["does_not_exist"] % smart_text(data)
|
||||
msg = self.error_messages["does_not_exist"] % smart_str(data)
|
||||
raise ValidationError(msg)
|
||||
except (TypeError, ValueError):
|
||||
received = type(data).__name__
|
||||
@@ -342,7 +342,7 @@ class SlugRelatedField(RelatedField):
|
||||
return self.queryset.get(**{self.slug_field: data})
|
||||
except ObjectDoesNotExist:
|
||||
raise ValidationError(self.error_messages["does_not_exist"] %
|
||||
(self.slug_field, smart_text(data)))
|
||||
(self.slug_field, smart_str(data)))
|
||||
except (TypeError, ValueError):
|
||||
msg = self.error_messages["invalid"]
|
||||
raise ValidationError(msg)
|
||||
|
@@ -44,7 +44,7 @@ from django.core.exceptions import ImproperlyConfigured
|
||||
from django.http.multipartparser import parse_header
|
||||
from django.template import RequestContext, loader, Template
|
||||
from django.test.client import encode_multipart
|
||||
from django.utils import six
|
||||
import six
|
||||
|
||||
from .utils import encoders
|
||||
|
||||
|
@@ -45,7 +45,7 @@ from django.conf import settings
|
||||
from django.http import QueryDict
|
||||
from django.http.multipartparser import parse_header
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
from django.utils.six import BytesIO
|
||||
import six
|
||||
|
||||
from taiga.base import exceptions
|
||||
|
||||
@@ -332,7 +332,7 @@ class Request(object):
|
||||
elif hasattr(self._request, "read"):
|
||||
self._stream = self._request
|
||||
else:
|
||||
self._stream = BytesIO(self.raw_post_data)
|
||||
self._stream = six.BytesIO(self.raw_post_data)
|
||||
|
||||
def _perform_form_overloading(self):
|
||||
"""
|
||||
@@ -367,7 +367,7 @@ class Request(object):
|
||||
self._CONTENT_PARAM in self._data and
|
||||
self._CONTENTTYPE_PARAM in self._data):
|
||||
self._content_type = self._data[self._CONTENTTYPE_PARAM]
|
||||
self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context["encoding"]))
|
||||
self._stream = six.BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context["encoding"]))
|
||||
self._data, self._files = (Empty, Empty)
|
||||
|
||||
def _parse(self):
|
||||
|
@@ -49,8 +49,8 @@ from django.apps import apps
|
||||
from django.core.paginator import Page
|
||||
from django.db import models
|
||||
from django.forms import widgets
|
||||
from django.utils import six
|
||||
from django.utils.translation import ugettext as _
|
||||
import six
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .settings import api_settings
|
||||
|
||||
|
@@ -52,7 +52,7 @@ back to the defaults.
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils import six
|
||||
import six
|
||||
|
||||
import importlib
|
||||
|
||||
|
@@ -205,7 +205,7 @@ class AnonRateThrottle(SimpleRateThrottle):
|
||||
if request.user.is_authenticated:
|
||||
return None # Only throttle unauthenticated requests.
|
||||
|
||||
ident = request.META.get("HTTP_X_FORWARDED_FOR")
|
||||
ident = request.headers.get("x-forwarded-for")
|
||||
if ident is None:
|
||||
ident = request.META.get("REMOTE_ADDR")
|
||||
|
||||
|
@@ -33,7 +33,7 @@
|
||||
|
||||
|
||||
from django.urls import URLResolver
|
||||
from django.conf.urls import url, include
|
||||
from django.urls import include, re_path
|
||||
|
||||
from .settings import api_settings
|
||||
|
||||
@@ -51,7 +51,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required):
|
||||
patterns = apply_suffix_patterns(urlpattern.url_patterns,
|
||||
suffix_pattern,
|
||||
suffix_required)
|
||||
ret.append(url(regex, include(patterns, namespace, app_name), kwargs))
|
||||
ret.append(re_path(regex, include(patterns, namespace, app_name), kwargs))
|
||||
|
||||
else:
|
||||
# Regular URL pattern
|
||||
@@ -62,7 +62,7 @@ def apply_suffix_patterns(urlpatterns, suffix_pattern, suffix_required):
|
||||
# Add in both the existing and the new urlpattern
|
||||
if not suffix_required:
|
||||
ret.append(urlpattern)
|
||||
ret.append(url(regex, view, kwargs, name))
|
||||
ret.append(re_path(regex, view, kwargs, name))
|
||||
|
||||
return ret
|
||||
|
||||
|
@@ -38,7 +38,7 @@ from django.db.models.query import QuerySet
|
||||
from django.utils.functional import Promise
|
||||
from django.utils import timezone
|
||||
# from django.utils.deprecation import CallableBool
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
import datetime
|
||||
import decimal
|
||||
@@ -55,7 +55,7 @@ class JSONEncoder(json.JSONEncoder):
|
||||
# For Date Time string spec, see ECMA 262
|
||||
# http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
|
||||
if isinstance(o, Promise):
|
||||
return force_text(o)
|
||||
return force_str(o)
|
||||
# elif isinstance(o, CallableBool):
|
||||
# return bool(o)
|
||||
elif isinstance(o, datetime.datetime):
|
||||
|
@@ -42,8 +42,8 @@ from django.http.response import HttpResponseBase
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.defaults import server_error
|
||||
from django.views.generic import View
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.encoding import smart_str
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from .request import Request
|
||||
from .settings import api_settings
|
||||
@@ -84,7 +84,7 @@ def get_view_description(view_cls, html=False):
|
||||
This function is the default for the `VIEW_DESCRIPTION_FUNCTION` setting.
|
||||
"""
|
||||
description = view_cls.__doc__ or ''
|
||||
description = formatting.dedent(smart_text(description))
|
||||
description = formatting.dedent(smart_str(description))
|
||||
if html:
|
||||
return formatting.markup_description(description)
|
||||
return description
|
||||
@@ -476,7 +476,7 @@ class APIView(View):
|
||||
|
||||
|
||||
def api_server_error(request, *args, **kwargs):
|
||||
if settings.DEBUG is False and request.META.get('CONTENT_TYPE', None) == "application/json":
|
||||
if settings.DEBUG is False and request.headers.get('content-type', None) == "application/json":
|
||||
return HttpResponse(json.dumps({"error": _("Server application error")}),
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return server_error(request, *args, **kwargs)
|
||||
|
@@ -33,7 +33,7 @@
|
||||
|
||||
from functools import update_wrapper
|
||||
from django.utils.decorators import classonlymethod
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from . import views
|
||||
from . import mixins
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
from taiga.base.exceptions import BaseException
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class ConnectorBaseException(BaseException):
|
||||
|
@@ -6,7 +6,7 @@
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.contrib.postgres.fields import JSONField as DjangoJSONField
|
||||
from django.db.models import JSONField as DjangoJSONField
|
||||
|
||||
|
||||
__all__ = ["JSONField"]
|
||||
|
@@ -41,8 +41,8 @@ In addition Django's built in 403 and 404 exceptions are handled.
|
||||
|
||||
from django.core.exceptions import PermissionDenied as DjangoPermissionDenied
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import force_str
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.http import Http404
|
||||
|
||||
from . import response
|
||||
@@ -222,7 +222,7 @@ def format_exception(exc):
|
||||
class_name = exc.__class__.__name__
|
||||
class_module = exc.__class__.__module__
|
||||
detail = {
|
||||
"_error_message": force_text(exc.detail),
|
||||
"_error_message": force_str(exc.detail),
|
||||
"_error_type": "{0}.{1}".format(class_module, class_name)
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,7 @@
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.forms import widgets
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from taiga.base.api import serializers, ISO_8601
|
||||
from taiga.base.api.settings import api_settings
|
||||
|
||||
|
@@ -12,7 +12,7 @@ from dateutil.parser import parse as parse_date
|
||||
from django.apps import apps
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base import exceptions as exc
|
||||
from taiga.base.api.utils import get_object_or_error
|
||||
|
@@ -45,7 +45,7 @@ class CorsMiddleware(object):
|
||||
response["Access-Control-Allow-Credentials"] = "true"
|
||||
|
||||
def process_request(self, request):
|
||||
if "HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META:
|
||||
if "access-control-request-method" in request.headers:
|
||||
response = http.HttpResponse()
|
||||
self._populate_response(response)
|
||||
return response
|
||||
|
@@ -9,7 +9,7 @@ from collections import namedtuple
|
||||
|
||||
from django.db import connection
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.models.sql.datastructures import EmptyResultSet
|
||||
from django.core.exceptions import EmptyResultSet
|
||||
from taiga.base.api import serializers
|
||||
from taiga.base.fields import Field, MethodField
|
||||
|
||||
|
@@ -37,7 +37,7 @@ from http.client import responses
|
||||
from django import http
|
||||
|
||||
from django.template.response import SimpleTemplateResponse
|
||||
from django.utils import six
|
||||
import six
|
||||
|
||||
|
||||
class Response(SimpleTemplateResponse):
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
import itertools
|
||||
from collections import namedtuple
|
||||
from django.conf.urls import url
|
||||
from django.urls import path, re_path
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.urls import NoReverseMatch, URLResolver
|
||||
|
||||
@@ -249,7 +249,7 @@ class SimpleRouter(BaseRouter):
|
||||
)
|
||||
view = viewset.as_view(mapping, **route.initkwargs)
|
||||
name = route.name.format(basename=basename)
|
||||
ret.append(url(regex, view, name=name))
|
||||
ret.append(re_path(regex, view, name=name))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -295,7 +295,7 @@ class DRFDefaultRouter(SimpleRouter):
|
||||
urls = []
|
||||
|
||||
if self.include_root_view:
|
||||
root_url = url(r'^$', self.get_api_root_view(), name=self.root_view_name)
|
||||
root_url = path('', self.get_api_root_view(), name=self.root_view_name)
|
||||
urls.append(root_url)
|
||||
|
||||
default_urls = super(DRFDefaultRouter, self).get_urls()
|
||||
|
@@ -15,8 +15,8 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
cleanup_pre_delete = Signal(providing_args=["file"])
|
||||
cleanup_post_delete = Signal(providing_args=["file"])
|
||||
cleanup_pre_delete = Signal()
|
||||
cleanup_post_delete = Signal()
|
||||
|
||||
|
||||
def _find_models_with_filefield():
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.encoding import force_text
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from taiga.base.api.utils import encoders
|
||||
|
||||
@@ -18,7 +18,7 @@ def dumps(data, ensure_ascii=True, encoder_class=encoders.JSONEncoder, indent=No
|
||||
|
||||
def loads(data):
|
||||
if isinstance(data, bytes):
|
||||
data = force_text(data)
|
||||
data = force_str(data)
|
||||
return json.loads(data)
|
||||
|
||||
load = json.load
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
@@ -11,7 +11,7 @@ from urllib.parse import urlparse
|
||||
|
||||
import django_sites as sites
|
||||
from django.urls import reverse as django_reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
URL_TEMPLATE = "{scheme}://{domain}/{path}"
|
||||
|
||||
|
@@ -5,4 +5,3 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.events.apps.EventsAppConfig"
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import collections
|
||||
|
||||
from django.db import connection
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
|
@@ -49,7 +49,7 @@ class SessionIDMiddleware(object):
|
||||
|
||||
def process_request(self, request):
|
||||
global _local
|
||||
session_id = request.META.get("HTTP_X_SESSION_ID", None)
|
||||
session_id = request.headers.get("x-session-id", None)
|
||||
_local.session_id = session_id
|
||||
request.session_id = session_id
|
||||
|
||||
|
@@ -10,7 +10,7 @@ import uuid
|
||||
import gzip
|
||||
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.db.transaction import atomic
|
||||
from django.db.models import signals
|
||||
from django.conf import settings
|
||||
@@ -66,7 +66,7 @@ class ProjectExporterViewSet(mixins.ImportThrottlingPolicyMixin, GenericViewSet)
|
||||
if dump_format == "gzip":
|
||||
path = "exports/{}/{}-{}.json.gz".format(project.pk, project.slug, uuid.uuid4().hex)
|
||||
with default_storage.open(path, mode="wb") as outfile:
|
||||
services.render_project(project, gzip.GzipFile(fileobj=outfile))
|
||||
services.render_project(project, gzip.GzipFile(fileobj=outfile, mode="wb"))
|
||||
else:
|
||||
path = "exports/{}/{}-{}.json".format(project.pk, project.slug, uuid.uuid4().hex)
|
||||
with default_storage.open(path, mode="wb") as outfile:
|
||||
|
@@ -17,7 +17,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db import utils
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.projects.history.services import make_key_from_model_object, take_snapshot
|
||||
from taiga.projects.models import Membership
|
||||
|
@@ -6,7 +6,7 @@
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
def has_available_slot_for_new_project(owner, is_private, member_emails):
|
||||
|
@@ -15,7 +15,7 @@ from django.core.files.base import ContentFile
|
||||
from django.utils import timezone
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.base.utils import json
|
||||
@@ -36,7 +36,7 @@ def dump_project(self, user, project, dump_format):
|
||||
if dump_format == "gzip":
|
||||
path = "exports/{}/{}-{}.json.gz".format(project.pk, project.slug, self.request.id)
|
||||
with default_storage.open(path, mode="wb") as outfile:
|
||||
services.render_project(project, gzip.GzipFile(fileobj=outfile))
|
||||
services.render_project(project, gzip.GzipFile(fileobj=outfile, mode="wb"))
|
||||
else:
|
||||
path = "exports/{}/{}-{}.json".format(project.pk, project.slug, self.request.id)
|
||||
with default_storage.open(path, mode="wb") as outfile:
|
||||
|
@@ -10,7 +10,7 @@ import copy
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from taiga.base.api import serializers
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.api import serializers
|
||||
from taiga.base.api import validators
|
||||
|
@@ -17,7 +17,7 @@ from taiga.base.api import ModelCrudViewSet, ModelRetrieveViewSet
|
||||
from taiga.base.api.utils import get_object_or_404
|
||||
from taiga.base.decorators import list_route, detail_route
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class Application(ModelRetrieveViewSet):
|
||||
|
@@ -15,10 +15,10 @@ class Token(BaseAuthentication):
|
||||
auth_rx = re.compile(r"^Application (.+)$")
|
||||
|
||||
def authenticate(self, request):
|
||||
if "HTTP_AUTHORIZATION" not in request.META:
|
||||
if "authorization" not in request.headers:
|
||||
return None
|
||||
|
||||
token_rx_match = self.auth_rx.search(request.META["HTTP_AUTHORIZATION"])
|
||||
token_rx_match = self.auth_rx.search(request.headers["authorization"])
|
||||
if not token_rx_match:
|
||||
return None
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
from django.conf import settings
|
||||
from django.core import signing
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import uuid
|
||||
|
||||
|
@@ -11,7 +11,7 @@ from taiga.base.fields import Field
|
||||
from . import models
|
||||
from . import services
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
class ApplicationSerializer(serializers.LightSerializer):
|
||||
|
@@ -9,7 +9,7 @@ from taiga.base import exceptions as exc
|
||||
from taiga.base.api.utils import get_object_or_404
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
|
||||
def get_user_for_application_token(token:str) -> object:
|
||||
|
@@ -5,4 +5,3 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.feedback.apps.FeedbackAppConfig"
|
||||
|
@@ -33,9 +33,9 @@ class FeedbackViewSet(viewsets.ViewSet):
|
||||
self.object = validator.save(force_insert=True)
|
||||
|
||||
extra = {
|
||||
"HTTP_HOST": request.META.get("HTTP_HOST", None),
|
||||
"HTTP_REFERER": request.META.get("HTTP_REFERER", None),
|
||||
"HTTP_USER_AGENT": request.META.get("HTTP_USER_AGENT", None),
|
||||
"HTTP_HOST": request.headers.get("host", None),
|
||||
"HTTP_REFERER": request.headers.get("referer", None),
|
||||
"HTTP_USER_AGENT": request.headers.get("user-agent", None),
|
||||
}
|
||||
services.send_feedback(self.object, extra, reply_to=[request.user.email])
|
||||
|
||||
|
@@ -8,7 +8,7 @@
|
||||
from django.apps import AppConfig
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from django.urls import include, path
|
||||
|
||||
|
||||
class FeedbackAppConfig(AppConfig):
|
||||
@@ -19,4 +19,4 @@ class FeedbackAppConfig(AppConfig):
|
||||
if settings.FEEDBACK_ENABLED:
|
||||
from taiga.urls import urlpatterns
|
||||
from .routers import router
|
||||
urlpatterns.append(url(r'^api/v1/', include(router.urls)))
|
||||
urlpatterns.append(path('api/v1/', include(router.urls)))
|
||||
|
@@ -6,7 +6,7 @@
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class FeedbackEntry(models.Model):
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base import exceptions as exc
|
||||
from taiga.base import response
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base import exceptions as exc
|
||||
@@ -62,4 +62,4 @@ class BitBucketViewSet(BaseWebhookApiViewSet):
|
||||
return project_secret == secret_key
|
||||
|
||||
def _get_event_name(self, request):
|
||||
return request.META.get('HTTP_X_EVENT_KEY', None)
|
||||
return request.headers.get('x-event-key', None)
|
||||
|
@@ -19,7 +19,7 @@ class BaseBitBucketEventHook():
|
||||
if wiki_text is None:
|
||||
wiki_text = ""
|
||||
|
||||
template = "\g<1>[BitBucket#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
|
||||
template = fr"\g<1>[BitBucket#\g<2>]({project_url}/issues/\g<2>)\g<3>"
|
||||
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class IssueCommentEventHook(BaseBitBucketEventHook, BaseIssueCommentEventHook):
|
||||
project_url = self.payload.get('repository', {}).get('links', {}).get('html', {}).get('href', None)
|
||||
issue_url = self.payload.get('issue', {}).get('links', {}).get('html', {}).get('href', None)
|
||||
comment_id = self.payload.get('comment', {}).get('id', None)
|
||||
comment_url = "{}#comment-{}".format(issue_url, comment_id)
|
||||
comment_url = f"{issue_url}#comment-{comment_id}"
|
||||
return {
|
||||
"number": self.payload.get('issue', {}).get('id', None),
|
||||
'url': issue_url,
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
import re
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.contrib.auth import get_user_model
|
||||
from taiga.projects.models import IssueStatus, TaskStatus, UserStoryStatus, EpicStatus, ProjectModulesConfig
|
||||
from taiga.projects.epics.models import Epic
|
||||
|
@@ -5,5 +5,4 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.hooks.github.apps.GithubHooksAppConfig"
|
||||
|
||||
|
@@ -21,7 +21,7 @@ class GitHubViewSet(BaseWebhookApiViewSet):
|
||||
}
|
||||
|
||||
def _validate_signature(self, project, request):
|
||||
x_hub_signature = request.META.get("HTTP_X_HUB_SIGNATURE", None)
|
||||
x_hub_signature = request.headers.get("x-hub-signature", None)
|
||||
if not x_hub_signature:
|
||||
return False
|
||||
|
||||
@@ -41,4 +41,4 @@ class GitHubViewSet(BaseWebhookApiViewSet):
|
||||
return hmac.compare_digest(mac.hexdigest(), signature)
|
||||
|
||||
def _get_event_name(self, request):
|
||||
return request.META.get("HTTP_X_GITHUB_EVENT", None)
|
||||
return request.headers.get("x-github-event", None)
|
||||
|
@@ -20,7 +20,7 @@ class BaseGitHubEventHook():
|
||||
if wiki_text is None:
|
||||
wiki_text = ""
|
||||
|
||||
template = "\g<1>[GitHub#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
|
||||
template = fr"\g<1>[GitHub#\g<2>]({project_url}/issues/\g<2>)\g<3>"
|
||||
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
|
||||
|
||||
|
||||
|
@@ -5,5 +5,4 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.hooks.gitlab.apps.GitLabHooksAppConfig"
|
||||
|
||||
|
@@ -21,7 +21,7 @@ class BaseGitLabEventHook():
|
||||
if wiki_text is None:
|
||||
wiki_text = ""
|
||||
|
||||
template = "\g<1>[GitLab#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
|
||||
template = fr"\g<1>[GitLab#\g<2>]({project_url}/issues/\g<2>)\g<3>"
|
||||
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
|
||||
|
||||
|
||||
|
@@ -29,7 +29,7 @@ class GogsViewSet(BaseWebhookApiViewSet):
|
||||
if secret is None:
|
||||
return False
|
||||
|
||||
signature = request.META.get("HTTP_X_GOGS_SIGNATURE", None)
|
||||
signature = request.headers.get("x-gogs-signature", None)
|
||||
if not signature: # Old format signature support (before 0.11 version)
|
||||
payload = self._get_payload(request)
|
||||
return payload.get('secret', None) == secret
|
||||
|
@@ -19,7 +19,7 @@ class BaseGogsEventHook():
|
||||
if wiki_text is None:
|
||||
wiki_text = ""
|
||||
|
||||
template = "\g<1>[Gogs#\g<2>]({}/issues/\g<2>)\g<3>".format(project_url)
|
||||
template = fr"\g<1>[Gogs#\g<2>]({project_url}/issues/\g<2>)\g<3>"
|
||||
return re.sub(r"(\s|^)#(\d+)(\s|$)", template, wiki_text, 0, re.M)
|
||||
|
||||
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base.api import viewsets
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.users.models import User
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base.api import viewsets
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.celery import app
|
||||
|
@@ -9,6 +9,9 @@ import datetime
|
||||
from collections import OrderedDict
|
||||
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils import timezone
|
||||
|
||||
from taiga.importers import services as import_service
|
||||
from taiga.projects.references.models import recalc_reference_counter
|
||||
from taiga.projects.models import Project, ProjectTemplate, Points
|
||||
from taiga.projects.userstories.models import UserStory, RolePoints
|
||||
@@ -18,8 +21,8 @@ from taiga.projects.epics.models import Epic, RelatedUserStory
|
||||
from taiga.projects.history.services import take_snapshot
|
||||
from taiga.timeline.rebuilder import rebuild_timeline
|
||||
from taiga.timeline.models import Timeline
|
||||
|
||||
from .common import JiraImporterCommon
|
||||
from taiga.importers import services as import_service
|
||||
|
||||
|
||||
class JiraAgileImporter(JiraImporterCommon):
|
||||
@@ -158,8 +161,8 @@ class JiraAgileImporter(JiraImporterCommon):
|
||||
estimated_finish=end_date,
|
||||
)
|
||||
Milestone.objects.filter(id=milestone.id).update(
|
||||
created_date=start_datetime or datetime.datetime.now(),
|
||||
modified_date=start_datetime or datetime.datetime.now(),
|
||||
created_date=start_datetime or timezone.now(),
|
||||
modified_date=start_datetime or timezone.now(),
|
||||
)
|
||||
return project
|
||||
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
import uuid
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base.api import viewsets
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.users.models import User
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base.api import viewsets
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.users.models import User
|
||||
|
@@ -7,7 +7,7 @@
|
||||
|
||||
import uuid
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
from django.conf import settings
|
||||
|
||||
from taiga.base.api import viewsets
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from requests_oauthlib import OAuth1Session, OAuth1
|
||||
from django.conf import settings
|
||||
|
@@ -8,7 +8,7 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from taiga.base.mails import mail_builder
|
||||
from taiga.users.models import User
|
||||
|
@@ -11,39 +11,32 @@
|
||||
|
||||
import re
|
||||
import markdown
|
||||
|
||||
from markdown import inlinepatterns
|
||||
from markdown.util import AtomicString
|
||||
from xml.etree import ElementTree as etree
|
||||
|
||||
# We can't re-use the built-in AutolinkPattern because we need to add protocols
|
||||
# to links without them.
|
||||
class AutolinkPattern(markdown.inlinepatterns.Pattern):
|
||||
class AutolinkPattern(inlinepatterns.Pattern):
|
||||
def handleMatch(self, m):
|
||||
el = markdown.util.etree.Element("a")
|
||||
el = etree.Element("a")
|
||||
|
||||
href = m.group(2)
|
||||
if not re.match('^(ftp|https?)://', href, flags=re.IGNORECASE):
|
||||
href = 'http://%s' % href
|
||||
el.set('href', self.unescape(href))
|
||||
|
||||
el.text = markdown.util.AtomicString(m.group(2))
|
||||
el.text = AtomicString(m.group(2))
|
||||
return el
|
||||
|
||||
|
||||
class AutolinkExtension(markdown.Extension):
|
||||
"""An extension that turns all URLs into links.
|
||||
|
||||
This is based on the web-only URL regex by John Gruber that's listed on
|
||||
http://daringfireball.net/2010/07/improved_regex_for_matching_urls (which is
|
||||
in the public domain).
|
||||
|
||||
This regex seems to line up pretty closely with GitHub's URL matching. There
|
||||
are only two cases I've found where they differ. In both cases, I've
|
||||
modified the regex slightly to bring it in line with GitHub's parsing:
|
||||
|
||||
* GitHub accepts FTP-protocol URLs.
|
||||
* GitHub only accepts URLs with protocols or "www.", whereas Gruber's regex
|
||||
accepts things like "foo.com/bar".
|
||||
"""
|
||||
"""An extension that turns all URLs into links."""
|
||||
def extendMarkdown(self, md):
|
||||
url_re = r'(?i)\b((?:(?:ftp|https?)://|www\d{0,3}[.])([^\s<>]+))'
|
||||
url_re = '(%s)' % '|'.join([
|
||||
r'<(?:([Ff][Tt][Pp])|([Hh][Tt])[Tt][Pp][Ss]?)://[^>]*>',
|
||||
r'\b(?:([Ff][Tt][Pp])|([Hh][Tt])[Tt][Pp][Ss]?)://[^)<>\s]+[^.,)<>\s]',
|
||||
r'\bwww\.[^)<>\s]+[^.,)<>\s]',
|
||||
])
|
||||
autolink = AutolinkPattern(url_re, md)
|
||||
md.inlinePatterns.add('gfm-autolink', autolink, '_end')
|
||||
md.inlinePatterns.register(autolink, 'autolink', 70)
|
||||
|
@@ -9,23 +9,26 @@
|
||||
# for details. All rights reserved. Use of this source code is governed by a
|
||||
# BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import markdown
|
||||
from markdown import Extension
|
||||
from markdown.inlinepatterns import Pattern
|
||||
from markdown.util import AtomicString
|
||||
from xml.etree import ElementTree as etree
|
||||
|
||||
|
||||
# We can't re-use the built-in AutomailPattern because we need to add mailto:.
|
||||
# We also don't care about HTML-encoding the email.
|
||||
class AutomailPattern(markdown.inlinepatterns.Pattern):
|
||||
class AutomailPattern(Pattern):
|
||||
def handleMatch(self, m):
|
||||
el = markdown.util.etree.Element("a")
|
||||
el = etree.Element("a")
|
||||
el.set('href', self.unescape('mailto:' + m.group(2)))
|
||||
el.text = markdown.util.AtomicString(m.group(2))
|
||||
el.text = AtomicString(m.group(2))
|
||||
return el
|
||||
|
||||
|
||||
class AutomailExtension(markdown.Extension):
|
||||
class AutomailExtension(Extension):
|
||||
"""An extension that turns all email addresses into links."""
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
mail_re = r'\b(?i)([a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]+)\b'
|
||||
mail_re = r'\b([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]+)\b'
|
||||
automail = AutomailPattern(mail_re, md)
|
||||
md.inlinePatterns.add('gfm-automail', automail, '_end')
|
||||
md.inlinePatterns.register(automail, 'automail', 70)
|
||||
|
@@ -160,9 +160,9 @@ class EmojifyExtension(Extension):
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
md.registerExtension(self)
|
||||
md.preprocessors.add('emojify',
|
||||
EmojifyPreprocessor(md),
|
||||
'_end')
|
||||
md.preprocessors.register(EmojifyPreprocessor(md),
|
||||
'emojify',
|
||||
70)
|
||||
|
||||
|
||||
class EmojifyPreprocessor(Preprocessor):
|
||||
|
@@ -32,7 +32,9 @@ from django.contrib.auth import get_user_model
|
||||
|
||||
from markdown.extensions import Extension
|
||||
from markdown.inlinepatterns import Pattern
|
||||
from markdown.util import etree, AtomicString
|
||||
from markdown.util import AtomicString
|
||||
from xml.etree import ElementTree as etree
|
||||
from taiga.front.templatetags.functions import resolve
|
||||
|
||||
|
||||
class MentionsExtension(Extension):
|
||||
@@ -43,10 +45,10 @@ class MentionsExtension(Extension):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
MENTION_RE = r"(@)([\w.-]+)"
|
||||
MENTION_RE = r"\B(@)([\w.-]+)\b"
|
||||
mentionsPattern = MentionsPattern(MENTION_RE, project=self.project)
|
||||
mentionsPattern.md = md
|
||||
md.inlinePatterns.add("mentions", mentionsPattern, "_end")
|
||||
md.inlinePatterns.register(mentionsPattern, "mentions", 80)
|
||||
|
||||
|
||||
class MentionsPattern(Pattern):
|
||||
@@ -66,7 +68,7 @@ class MentionsPattern(Pattern):
|
||||
except get_user_model().DoesNotExist:
|
||||
return "@{}".format(username)
|
||||
|
||||
url = "/profile/{}".format(username)
|
||||
url = resolve("user", username)
|
||||
|
||||
link_text = "@{}".format(username)
|
||||
|
||||
|
@@ -31,7 +31,7 @@
|
||||
|
||||
from markdown.extensions import Extension
|
||||
from markdown.inlinepatterns import Pattern
|
||||
from markdown.util import etree
|
||||
from xml.etree import ElementTree as etree
|
||||
|
||||
from taiga.projects.references.services import get_instance_by_ref
|
||||
from taiga.front.templatetags.functions import resolve
|
||||
@@ -46,7 +46,7 @@ class TaigaReferencesExtension(Extension):
|
||||
TAIGA_REFERENCE_RE = r'(?<=^|(?<=[^a-zA-Z0-9-\[]))#(\d+)'
|
||||
referencesPattern = TaigaReferencesPattern(TAIGA_REFERENCE_RE, self.project)
|
||||
referencesPattern.md = md
|
||||
md.inlinePatterns.add('taiga-references', referencesPattern, '_begin')
|
||||
md.inlinePatterns.register(referencesPattern, 'taiga-references', 65)
|
||||
|
||||
|
||||
class TaigaReferencesPattern(Pattern):
|
||||
|
@@ -21,9 +21,9 @@ class RefreshAttachmentExtension(markdown.Extension):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
md.treeprocessors.add("refresh_attachment",
|
||||
RefreshAttachmentTreeprocessor(md, project=self.project),
|
||||
"<prettify")
|
||||
md.treeprocessors.register(RefreshAttachmentTreeprocessor(md, project=self.project),
|
||||
"refresh_attachment",
|
||||
20)
|
||||
|
||||
|
||||
class RefreshAttachmentTreeprocessor(Treeprocessor):
|
||||
|
@@ -35,5 +35,5 @@ class SemiSaneListExtension(markdown.Extension):
|
||||
"""
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
md.parser.blockprocessors['olist'] = SemiSaneOListProcessor(md.parser)
|
||||
md.parser.blockprocessors['ulist'] = SemiSaneUListProcessor(md.parser)
|
||||
md.parser.blockprocessors.register(SemiSaneOListProcessor(md.parser), 'olist', 39)
|
||||
md.parser.blockprocessors.register(SemiSaneUListProcessor(md.parser), 'ulist', 29)
|
||||
|
@@ -22,4 +22,4 @@ class StrikethroughExtension(markdown.Extension):
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
pattern = markdown.inlinepatterns.SimpleTagPattern(STRIKE_RE, 'del')
|
||||
md.inlinePatterns.add('gfm-strikethrough', pattern, '_end')
|
||||
md.inlinePatterns.register(pattern, 'strikethrough', 70)
|
||||
|
@@ -16,9 +16,9 @@ from taiga.front.templatetags.functions import resolve
|
||||
class TargetBlankLinkExtension(markdown.Extension):
|
||||
"""An extension that add target="_blank" to all external links."""
|
||||
def extendMarkdown(self, md):
|
||||
md.treeprocessors.add("target_blank_links",
|
||||
TargetBlankLinksTreeprocessor(md),
|
||||
"<prettify")
|
||||
md.treeprocessors.register(TargetBlankLinksTreeprocessor(md),
|
||||
"target_blank_links",
|
||||
10)
|
||||
|
||||
|
||||
class TargetBlankLinksTreeprocessor(Treeprocessor):
|
||||
@@ -28,6 +28,7 @@ class TargetBlankLinksTreeprocessor(Treeprocessor):
|
||||
for a in links:
|
||||
href = a.get("href", "")
|
||||
url = a.get("href", "")
|
||||
|
||||
if url.endswith("/"):
|
||||
url = url[:-1]
|
||||
|
||||
|
@@ -8,8 +8,7 @@
|
||||
from markdown import Extension
|
||||
from markdown.inlinepatterns import Pattern
|
||||
from markdown.treeprocessors import Treeprocessor
|
||||
|
||||
from markdown.util import etree
|
||||
from xml.etree import ElementTree as etree
|
||||
|
||||
from taiga.front.templatetags.functions import resolve
|
||||
from taiga.base.utils.slug import slugify
|
||||
@@ -24,12 +23,12 @@ class WikiLinkExtension(Extension):
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
WIKILINK_RE = r"\[\[([\w0-9_ -]+)(\|[^\]]+)?\]\]"
|
||||
md.inlinePatterns.add("wikilinks",
|
||||
WikiLinksPattern(md, WIKILINK_RE, self.project),
|
||||
"<not_strong")
|
||||
md.treeprocessors.add("relative_to_absolute_links",
|
||||
RelativeLinksTreeprocessor(md, self.project),
|
||||
"<prettify")
|
||||
md.inlinePatterns.register(WikiLinksPattern(md, WIKILINK_RE, self.project),
|
||||
"wikilinks",
|
||||
20)
|
||||
md.treeprocessors.register(RelativeLinksTreeprocessor(md, self.project),
|
||||
"relative_to_absolute_links",
|
||||
20)
|
||||
|
||||
|
||||
class WikiLinksPattern(Pattern):
|
||||
|
@@ -57,6 +57,8 @@ bleach.ALLOWED_ATTRIBUTES["img"] = ["alt", "src"]
|
||||
bleach.ALLOWED_ATTRIBUTES["input"] = ["type", "checked"]
|
||||
bleach.ALLOWED_ATTRIBUTES["*"] = ["class", "style", "id"]
|
||||
|
||||
ALLOWED_PROTOCOLS = ["http", "https", "ftp", "mailto"]
|
||||
|
||||
|
||||
def _make_extensions_list(project=None):
|
||||
return ["pymdownx.tasklist",
|
||||
@@ -126,12 +128,12 @@ def _get_markdown(project):
|
||||
@cache_by_sha
|
||||
def render(project, text):
|
||||
md = _get_markdown(project)
|
||||
return bleach.clean(md.convert(text))
|
||||
return bleach.clean(md.convert(text), protocols=ALLOWED_PROTOCOLS)
|
||||
|
||||
|
||||
def render_and_extract(project, text):
|
||||
md = _get_markdown(project)
|
||||
result = bleach.clean(md.convert(text))
|
||||
result = bleach.clean(md.convert(text), protocols=ALLOWED_PROTOCOLS)
|
||||
return (result, md.extracted_data)
|
||||
|
||||
|
||||
|
@@ -6,12 +6,12 @@
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django_jinja import library
|
||||
from jinja2 import Markup
|
||||
from jinja2.utils import markupsafe
|
||||
from taiga.mdrender.service import render
|
||||
|
||||
|
||||
@library.global_function
|
||||
def mdrender(project, text) -> str:
|
||||
if text:
|
||||
return Markup(render(project, text))
|
||||
return markupsafe.Markup(render(project, text))
|
||||
return ""
|
||||
|
@@ -5,7 +5,7 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
ANON_PERMISSIONS = [
|
||||
('view_project', _('View project')),
|
||||
|
@@ -5,4 +5,3 @@
|
||||
#
|
||||
# Copyright (c) 2021-present Kaleidos Ventures SL
|
||||
|
||||
default_app_config = "taiga.projects.apps.ProjectsAppConfig"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user