diff --git a/requirements-dev.txt b/requirements-dev.txt index b432f168..292fc59b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -factory-boy==2.12.0 -mkdocs==1.1 -pytest==5.4.1 -pytest-django==3.8.0 +factory-boy==3.2.0 +mkdocs==1.1.2 +pytest==6.2.4 +pytest-django==4.3.0 diff --git a/requirements.txt b/requirements.txt index 83d58ebc..fc44aa42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -Django==2.2.17 +Django==3.2.3 django-agnocomplete==1.0.0 -django-compressor==2.4 -Pillow==8.0.1 -psycopg2==2.8.4 -requests==2.23.0 -social-auth-core==3.3.2 -social-auth-app-django==3.1.0 +django-compressor==2.4.1 +Pillow==8.2.0 +psycopg2==2.8.6 +requests==2.25.1 +social-auth-core==4.1.0 +social-auth-app-django==4.0.0 diff --git a/umap/fields.py b/umap/fields.py index 064bd2f3..00dbf1e5 100644 --- a/umap/fields.py +++ b/umap/fields.py @@ -1,6 +1,6 @@ import json -from django.utils import six +import six from django.db import models from django.utils.encoding import smart_text diff --git a/umap/forms.py b/umap/forms.py index 10efa09f..09d0ab3a 100644 --- a/umap/forms.py +++ b/umap/forms.py @@ -1,7 +1,7 @@ from django import forms from django.contrib.gis.geos import Point from django.contrib.auth import get_user_model -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.template.defaultfilters import slugify from django.conf import settings from django.forms.utils import ErrorList diff --git a/umap/middleware.py b/umap/middleware.py index dc76490f..d4c0e81e 100644 --- a/umap/middleware.py +++ b/umap/middleware.py @@ -1,7 +1,7 @@ from django.conf import settings from django.core.exceptions import MiddlewareNotUsed from django.http import HttpResponseForbidden -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ def readonly_middleware(get_response): diff --git a/umap/models.py b/umap/models.py index b77f2394..e0935a4d 100644 --- a/umap/models.py +++ b/umap/models.py @@ -4,7 +4,7 @@ import time from django.contrib.gis.db import models from django.conf import settings from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from django.core.signing import Signer from django.template.defaultfilters import slugify from django.core.files.base import File diff --git a/umap/settings/__init__.py b/umap/settings/__init__.py index 2d65ffc2..fc25bed1 100644 --- a/umap/settings/__init__.py +++ b/umap/settings/__init__.py @@ -1,6 +1,6 @@ -import imp import os import sys +import types from django.utils.termcolors import colorize @@ -22,7 +22,7 @@ if not path: print(colorize(msg, fg='red')) sys.exit(1) -d = imp.new_module('config') +d = types.ModuleType('config') d.__file__ = path try: with open(path) as config_file: diff --git a/umap/settings/base.py b/umap/settings/base.py index baaa1c3c..bb1b7591 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -1,6 +1,5 @@ """Base settings shared by all environments""" # Import global settings to make it easier to extend settings. -from django.conf.global_settings import * # pylint: disable=W0614,W0401 from django.template.defaultfilters import slugify from django.conf.locale import LANG_INFO @@ -130,8 +129,10 @@ STATIC_ROOT = os.path.join('static') MEDIA_ROOT = os.path.join('uploads') STATICFILES_FINDERS = [ + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 'compressor.finders.CompressorFinder', -] + STATICFILES_FINDERS +] # ============================================================================= # Templates diff --git a/umap/tests/base.py b/umap/tests/base.py index b2c64942..24271178 100644 --- a/umap/tests/base.py +++ b/umap/tests/base.py @@ -10,14 +10,14 @@ from umap.models import DataLayer, Licence, Map, TileLayer User = get_user_model() -class LicenceFactory(factory.DjangoModelFactory): +class LicenceFactory(factory.django.DjangoModelFactory): name = "WTFPL" class Meta: model = Licence -class TileLayerFactory(factory.DjangoModelFactory): +class TileLayerFactory(factory.django.DjangoModelFactory): name = "Test zoom layer" url_template = "http://{s}.test.org/{z}/{x}/{y}.png" attribution = "Test layer attribution" @@ -26,7 +26,7 @@ class TileLayerFactory(factory.DjangoModelFactory): model = TileLayer -class UserFactory(factory.DjangoModelFactory): +class UserFactory(factory.django.DjangoModelFactory): username = 'Joe' email = factory.LazyAttribute( lambda a: '{0}@example.com'.format(a.username).lower()) @@ -36,7 +36,7 @@ class UserFactory(factory.DjangoModelFactory): model = User -class MapFactory(factory.DjangoModelFactory): +class MapFactory(factory.django.DjangoModelFactory): name = "test map" slug = "test-map" center = DEFAULT_CENTER @@ -76,7 +76,7 @@ class MapFactory(factory.DjangoModelFactory): model = Map -class DataLayerFactory(factory.DjangoModelFactory): +class DataLayerFactory(factory.django.DjangoModelFactory): map = factory.SubFactory(MapFactory) name = "test datalayer" description = "test description" diff --git a/umap/urls.py b/umap/urls.py index 3d44c2db..e0ff193b 100644 --- a/umap/urls.py +++ b/umap/urls.py @@ -1,5 +1,5 @@ from django.conf import settings -from django.conf.urls import include, url +from django.conf.urls import include, re_path from django.conf.urls.i18n import i18n_patterns from django.conf.urls.static import static from django.contrib import admin @@ -17,74 +17,74 @@ from .utils import decorated_patterns admin.autodiscover() urlpatterns = [ - url(r'^admin/', admin.site.urls), - url('', include('social_django.urls', namespace='social')), - url(r'^m/(?P\d+)/$', views.MapShortUrl.as_view(), + re_path(r'^admin/', admin.site.urls), + re_path('', include('social_django.urls', namespace='social')), + re_path(r'^m/(?P\d+)/$', views.MapShortUrl.as_view(), name='map_short_url'), - url(r'^ajax-proxy/$', cache_page(180)(views.ajax_proxy), + re_path(r'^ajax-proxy/$', cache_page(180)(views.ajax_proxy), name='ajax-proxy'), - url(r'^change-password/', auth_views.PasswordChangeView.as_view(), + re_path(r'^change-password/', auth_views.PasswordChangeView.as_view(), {'template_name': 'umap/password_change.html'}, name='password_change'), - url(r'^change-password-done/', auth_views.PasswordChangeDoneView.as_view(), + re_path(r'^change-password-done/', auth_views.PasswordChangeDoneView.as_view(), {'template_name': 'umap/password_change_done.html'}, name='password_change_done'), - url(r'^i18n/', include('django.conf.urls.i18n')), - url(r'^agnocomplete/', include('agnocomplete.urls')), + re_path(r'^i18n/', include('django.conf.urls.i18n')), + re_path(r'^agnocomplete/', include('agnocomplete.urls')), ] i18n_urls = [ - url(r'^login/$', jsonize_view(auth_views.LoginView.as_view()), name='login'), # noqa - url(r'^login/popup/end/$', views.LoginPopupEnd.as_view(), + re_path(r'^login/$', jsonize_view(auth_views.LoginView.as_view()), name='login'), # noqa + re_path(r'^login/popup/end/$', views.LoginPopupEnd.as_view(), name='login_popup_end'), - url(r'^logout/$', views.logout, name='logout'), - url(r'^map/(?P\d+)/geojson/$', views.MapViewGeoJSON.as_view(), + re_path(r'^logout/$', views.logout, name='logout'), + re_path(r'^map/(?P\d+)/geojson/$', views.MapViewGeoJSON.as_view(), name='map_geojson'), - url(r'^map/anonymous-edit/(?P.+)$', + re_path(r'^map/anonymous-edit/(?P.+)$', views.MapAnonymousEditUrl.as_view(), name='map_anonymous_edit_url'), - url(r'^pictogram/json/$', views.PictogramJSONList.as_view(), + re_path(r'^pictogram/json/$', views.PictogramJSONList.as_view(), name='pictogram_list_json'), ] i18n_urls += decorated_patterns(cache_control(must_revalidate=True), - url(r'^datalayer/(?P[\d]+)/$', views.DataLayerView.as_view(), name='datalayer_view'), # noqa - url(r'^datalayer/(?P[\d]+)/versions/$', views.DataLayerVersions.as_view(), name='datalayer_versions'), # noqa - url(r'^datalayer/(?P[\d]+)/(?P[_\w]+.geojson)$', views.DataLayerVersion.as_view(), name='datalayer_version'), # noqa + re_path(r'^datalayer/(?P[\d]+)/$', views.DataLayerView.as_view(), name='datalayer_view'), # noqa + re_path(r'^datalayer/(?P[\d]+)/versions/$', views.DataLayerVersions.as_view(), name='datalayer_versions'), # noqa + re_path(r'^datalayer/(?P[\d]+)/(?P[_\w]+.geojson)$', views.DataLayerVersion.as_view(), name='datalayer_version'), # noqa ) i18n_urls += decorated_patterns([ensure_csrf_cookie], - url(r'^map/(?P[-_\w]+)_(?P\d+)$', views.MapView.as_view(), name='map'), # noqa - url(r'^map/new/$', views.MapNew.as_view(), name='map_new'), + re_path(r'^map/(?P[-_\w]+)_(?P\d+)$', views.MapView.as_view(), name='map'), # noqa + re_path(r'^map/new/$', views.MapNew.as_view(), name='map_new'), ) i18n_urls += decorated_patterns( [login_required_if_not_anonymous_allowed, never_cache], - url(r'^map/create/$', views.MapCreate.as_view(), name='map_create'), + re_path(r'^map/create/$', views.MapCreate.as_view(), name='map_create'), ) i18n_urls += decorated_patterns( [map_permissions_check, never_cache], - url(r'^map/(?P[\d]+)/update/settings/$', views.MapUpdate.as_view(), + re_path(r'^map/(?P[\d]+)/update/settings/$', views.MapUpdate.as_view(), name='map_update'), - url(r'^map/(?P[\d]+)/update/permissions/$', + re_path(r'^map/(?P[\d]+)/update/permissions/$', views.UpdateMapPermissions.as_view(), name='map_update_permissions'), - url(r'^map/(?P[\d]+)/update/owner/$', + re_path(r'^map/(?P[\d]+)/update/owner/$', views.AttachAnonymousMap.as_view(), name='map_attach_owner'), - url(r'^map/(?P[\d]+)/update/delete/$', + re_path(r'^map/(?P[\d]+)/update/delete/$', views.MapDelete.as_view(), name='map_delete'), - url(r'^map/(?P[\d]+)/update/clone/$', + re_path(r'^map/(?P[\d]+)/update/clone/$', views.MapClone.as_view(), name='map_clone'), - url(r'^map/(?P[\d]+)/datalayer/create/$', + re_path(r'^map/(?P[\d]+)/datalayer/create/$', views.DataLayerCreate.as_view(), name='datalayer_create'), - url(r'^map/(?P[\d]+)/datalayer/update/(?P\d+)/$', + re_path(r'^map/(?P[\d]+)/datalayer/update/(?P\d+)/$', views.DataLayerUpdate.as_view(), name='datalayer_update'), - url(r'^map/(?P[\d]+)/datalayer/delete/(?P\d+)/$', + re_path(r'^map/(?P[\d]+)/datalayer/delete/(?P\d+)/$', views.DataLayerDelete.as_view(), name='datalayer_delete'), ) urlpatterns += i18n_patterns( - url(r'^$', views.home, name="home"), - url(r'^showcase/$', cache_page(24 * 60 * 60)(views.showcase), + re_path(r'^$', views.home, name="home"), + re_path(r'^showcase/$', cache_page(24 * 60 * 60)(views.showcase), name='maps_showcase'), - url(r'^search/$', views.search, name="search"), - url(r'^about/$', views.about, name="about"), - url(r'^user/(?P.+)/$', views.user_maps, name='user_maps'), - url(r'', include(i18n_urls)), + re_path(r'^search/$', views.search, name="search"), + re_path(r'^about/$', views.about, name="about"), + re_path(r'^user/(?P.+)/$', views.user_maps, name='user_maps'), + re_path(r'', include(i18n_urls)), ) if settings.DEBUG and settings.MEDIA_ROOT: diff --git a/umap/utils.py b/umap/utils.py index 57d4ce30..46da7749 100644 --- a/umap/utils.py +++ b/umap/utils.py @@ -109,3 +109,7 @@ def gzip_file(from_path, to_path): with open(from_path, 'rb') as f_in: with gzip.open(to_path, 'wb') as f_out: f_out.writelines(f_in) + + +def is_ajax(request): + return request.headers.get('x-requested-with') == 'XMLHttpRequest' diff --git a/umap/views.py b/umap/views.py index 344db789..56eb50ca 100644 --- a/umap/views.py +++ b/umap/views.py @@ -23,7 +23,7 @@ from django.template.loader import render_to_string from django.urls import reverse, reverse_lazy from django.utils.encoding import force_bytes, smart_bytes from django.utils.http import http_date -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from django.utils.translation import to_locale from django.views.generic import DetailView, TemplateView, View from django.views.generic.base import RedirectView @@ -35,7 +35,7 @@ from .forms import (DEFAULT_LATITUDE, DEFAULT_LONGITUDE, DEFAULT_CENTER, AnonymousMapPermissionsForm, DataLayerForm, FlatErrorList, MapSettingsForm, UpdateMapPermissionsForm) from .models import DataLayer, Licence, Map, Pictogram, TileLayer -from .utils import get_uri_template, gzip_file +from .utils import get_uri_template, gzip_file, is_ajax try: # python3 @@ -116,7 +116,7 @@ class Home(TemplateView, PaginatorMixin): """ Dispatch template according to the kind of request: ajax or normal. """ - if self.request.is_ajax(): + if is_ajax(self.request): return [self.list_template_name] else: return [self.template_name] @@ -159,7 +159,7 @@ class UserMaps(DetailView, PaginatorMixin): """ Dispatch template according to the kind of request: ajax or normal. """ - if self.request.is_ajax(): + if is_ajax(self.request): return [self.list_template_name] else: return super(UserMaps, self).get_template_names() @@ -192,7 +192,7 @@ class Search(TemplateView, PaginatorMixin): """ Dispatch template according to the kind of request: ajax or normal. """ - if self.request.is_ajax(): + if is_ajax(self.request): return [self.list_template_name] else: return super(Search, self).get_template_names() @@ -239,7 +239,7 @@ showcase = MapsShowCase.as_view() def validate_url(request): assert request.method == "GET" - assert request.is_ajax() + assert is_ajax(request) url = request.GET.get('url') assert url try: